您现在的位置是:主页 > news > wordpress接入七牛云/独立站seo是什么
wordpress接入七牛云/独立站seo是什么
admin2025/6/7 14:30:32【news】
简介wordpress接入七牛云,独立站seo是什么,南通做网站的,网络优化的手段有哪些空指针3月公开赛已于上周六结束,感谢Muuu Nya以及yypex提供的WP。converter整体出题思路是:1. CVE-2016-37142. padding oracle由于题目提供了源代码, 直接开始审计,审计go代码, 首先找入口点, 然后一步一步跟着调。题目中的路由在 helper.go…
空指针3月公开赛已于上周六结束,感谢Muuu Nya以及yypex提供的WP。
converter整体出题思路是:
1. CVE-2016-3714
2. padding oracle
由于题目提供了源代码, 直接开始审计,审计go代码, 首先找入口点, 然后一步一步跟着调。题目中的路由在 helper.go中:
功能非常简单, 分别为
/api/list -> 列出当前用户都上传了什么文件
/api/new -> 新建上传请求, 检查上传文件是否合法
/api/convert -> 进行转换操作
在创建上传请求过程中, 程序总共进行了如下几个步骤:
1. 文件名是否合法(`checkFilenameSecure`)
2. 文件类型是否合法(`checkFileTypeSecure`)
3. 即将转换过去的文件类型是否合法(`checkFileDestTypeSecure`)
4. 保存用户上传的文件(`SaveUploadedFile`)
5. 将json序列化后上传请求加密, 回传给客户端(`json.Marshal` 和 `encrypt` )
在上传请求结束后, 自动重定向到转换功能路由, 并进行了如下操作:
1. 解密并反序列化json数据(`decrypt` 和 `json.Unmarshal`)
2. 检查源文件名, 目的文件名是否合法(``checkFilenameSecure` )
3. 拼接目的文件名, 转换(`generateRes`)
逐步进行审计
1. `checkFilenameSecure`
将附件保存文件夹的根地址与可控的文件名连接, 并判断和附件保存文件夹的根地址的相对路径是否以 `'./'` 开始, 应该没什么问题
2. `checkFileTypeSecure`
通过 `goimghdr.WhatFromReader` 库判断文件类型是否为 `"jpeg", "png", "gif", "bmp"` 之一
3. `checkFileDestTypeSecure`
通过判断post表单中的type是否合法, 看起来也没什么问题
4. `encrypt`
跟到 `aes.go` 中, 发现非常明显的人为创造出来的可以被 `padding oracle` 攻击利用的漏洞
5. `decrypt`
同上
6. `generateRes`
通过"[gopkg.in/gographics/imagick.v2/imagick](http://gopkg.in/gographics/imagick.v2/imagick)"库, 进行图片文件的类型转换, 发现这个库是使用的 ImageMagick`完成的转换过程, 使用的 ImageMagick 为6.x.x 版本
那么现在来总结一下我们的发现:
1. Padding Oracle 可以伪造任意密文
2. 伪造之后, 转换的文件类型, 文件名, 转换前的文件名均可控
3. 在网上搜索ImageMagick6漏洞
发现了CVE-2016-3714, 漏洞的产生原因是imagemagick没过滤文件名, 直接将未经处理的文件名拼接到了要执行的命令中, 从而导致了这个漏洞.
通过观察ubuntu默认安装的ImageMagick-6的delegates(运行 `identify -list delegate` 命令), 我们发现引发漏洞的 `%M` 这个参数依然在一堆命令中有用到 :
继续深入查 `/etc/ImageMagick-6/delegates.xml`时, 发现miff这个文件类型可以通过 `.show, .win`两种扩展名触发
以及根据 `mpeg:encode` 可以通过 `.mp4, .mpeg` 等等文件触发
现在就有了整体的利用思路
首先由于%M利用的是原文件名, 所以我们先将文件名含有RCE PoC的图片上传上去, 比如我这里使用的是
test';echo Y3AgL2ZsYWcgL2FwcC9hdHRhY2htZW50cy8x|base64 -d|sh;#'
再通过padding oracle伪造密文, 得到转换参数, 将我们想要的文件格式传进去, 我这里使用的是 .show这个格式
{"filename":"test';echo Y3AgL2ZsYWcgL2FwcC9hdHRhY2htZW50cy8x|base64 -d|sh;#'","type":"show"}
padding oracle伪造密文的脚本在github上有, 我们找一个修改一下就可以直接用了. 我这里选用的库是[https://github.com/lesterpotter/AesOracle](https://github.com/lesterpotter/AesOracle)
最后在完成密文伪造后, 就可以把伪造的密文传给 `/api/convert` 去进行转换, 最终完成命令执行,完整的利用代码在:
https://gist.github.com/ManassehZhou/09a6277810141059502dc9631833cdad
非预期
比赛过程中只有一人解出此题,并且用的还是非预期做法,十分巧妙(
经@zyssae授权, 引用非预期解法writeup)。
文件类型检查用的是 `corona10/goimghdr` 这个库,查看源码发现其只做了简单的检查,如下:
func whatImpl(r io.Reader) (string, error) { var what string hdr := make([]byte, 32) nBytes, err := r.Read(hdr) if err != nil { return "", err } if nBytes < 32 { msg := fmt.Sprintf("The target file size should be at least 32 bytes: %d bytes", nBytes) return "", errors.New(msg) } h := string(hdr) switch { case isJpeg(h): what = jpeg case isExif(h): what = jpeg ... } return what, nil } func isJpeg(h string) bool { return h[6:10] == "JFIF" } func isExif(h string) bool { return h[6:10] == "Exif" }
于是只要将文件 [6:10] 的数据改为 "JFIF" 或 "Exif" 即可绕过类型检查。然后查看文件格式,发现 svg 文件可以进行伪造。
<svg width="640px" height="480px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
svg的格式和xml类似, 我们可以给它添加任意的属性, 那么通过将第6-10位设置成 `JFIF` 就可以完成文件类型的伪造了.
注: 这里是因为 `corona10/goimghdr`中没有实现文本格式的图片类型的识别, 所以只能再简单判断过后, 将图片当成 jpg, 然后在 ImageMagick里面, 图片被识别为了svg格式. 所以绕过了全部的考点Orz
接下来要找读 flag 的方法,使用 ``可以引入外部 url 并且能触发 ssrf,测试一下这里能否使用之前公开过的任意文件读取漏洞:``,发现不行。
那有没有其他的伪协议呢?翻一下 ImageMagick 文档有一个 text 格式,可以读入文件内容,再次尝试就拿到了 flag。
PoC:
<svg Exif="" width="640px" height="480px" version="1.1"xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image xlink:href="text:/flag" x="0" y="0" height="640px" width="480px"/>svg>
RE
GatesXGame本题作为逆向公开赛的第一题,其难度较低,选手只要掌握下述技能就可轻松解题:
- 基础的花指令去除
- 任意一种图搜索算法(或十足的耐心)
本题核心算法是一个 10x10 的迷宫,从左上角出发,输入控制路径,到达右下角的唯一路径即为 flag。
这部分的迷宫代码生成后,由运行在 Wow64 下的主程序加载并执行,代码与主程序共享一些参数信息,在进入代码前由主程序设置:
//edx --> char* flag//ecx --> int idx//edi --> payload_base//ebx --> MAX_STEP (len(flag))
迷宫的 grids 使用了两种不同架构的代码块进行实现(x86 与 x64),第一个 grid(左上角)代码架构为 x86,保证每一个 grid 与其邻居的架构相异后,每一次移动都将需要一次架构的切换:
// open_gate_template_x86push 0x33 // cs:0x33sub esp, 4mov dword ptr ss:[esp], grid_dst_ip // next gridadd qword ptr ss:[esp], edi // payload_baseinc ecx // step++retf// open_gate_template_x64push 0x23 // cs:0x23sub rsp, 8mov qword ptr ss:[rsp], grid_dst_ipadd qword ptr ss:[rsp], rdiinc rcx // step++retfq
架构的切换对于运行在 Wow64 环境下的程序是必不可少的,运行在 64 位 Windows 系统下的 32 位程序在进入系统调用前需要完成下述操作:
32-bit ntdll.dll -> wow64cpu.dll’s Heaven’s Gate -> 64-bit ntdll.dll -> syscall into the kernel
对于本题来说,把每一次切换的操作看成是一次 gate open,正确地穿过所有的 gate 就能得到 flag,这也是题目名称的由来。
在 payload 部分以及主程序部分都穿插了少量低强度花指令,例如上述架构切换代码(x86->x64)可以变异为:
b = random.randint(0, 0x7f)a = 0x33 ^ bopen_gate_template_x86_mutated = f""" open_gate_label: push {hex(a)} xor dword ptr ss:[esp], {hex(b)} call next_label // sub esp, 4 .byte {random.randint(0,255)} next_label: mov dword ptr ss:[esp], grid_dst_ip add dword ptr ss:[esp], edi inc ecx retf
这一部分代码变异在写 payload generator 的时候做,写的时候要注意保证随机的元素不会改变 code block 的 size,否则可能会影响到最后的链接过程,例如 `push 0x7f` 和 `push 0x80` 两条指令,其指令长度就不一样:
push 7F | 6A 7Fpush 80 | 68 80000000
主程序中只有两种花指令,以 inline asm 的形式穿插在验证逻辑中:
#define JUNK2(idx) __asm{ \ __asm call next1_junk2_##idx \ __asm __emit 0x77 \ __asm jmp next_junk2_##idx \ __asm __emit 0x88 \ __asm next1_junk2_##idx: \ __asm add dword ptr ss:[esp], 1 \ __asm ret \ __asm next_junk2_##idx: \}#define JUNK1(idx) __asm{\__asm jmp jlabel##idx \__asm __emit 0x88 \__asm jlabel_##idx : \__asm ret \__asm __emit 0xba \__asm jlabel##idx : \__asm call jlabel_##idx \}
上述花指令都可以简单地通过模板匹配来去除,去除之后解题就只剩下一个迷宫要走了。迷宫并不复杂,为了降低难度在生成迷宫的时候我将 N 调到了 10,在导出迷宫数据之后,可以写搜索算法求解,身体强壮的选手也可以直接手动走完迷宫得到 flag。
调试相关虽然要解出本题并不太需要调试,但可能还是有部分选手对如何调试 payload 部分代码或者类似代码存有疑问,在提交的 wp 中还看到有选手把 `retf` 称为系统调用,这显然是不对的,有疑问的选手可以阅读下面的拓展内容:
http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/
https://medium.com/@fsx30/hooking-heavens-gate-a-wow64-hooking-technique-5235e1aeed73
https://www.malwaretech.com/2014/02/the-0x33-segment-selector-heavens-gate.html
Windows 下笔者到目前为止只发现一种调试器可以对需要进行架构切换的控制流进行跟踪,即 WinDbg(x64)。在架构切换处 step-in 之后调试器将会自动切换到另一架构模式(寄存器、栈、地址空间等都会自动适配)。如果要调试 payload 部分代码,可以将主程序逻辑中根据环境变量检测调试器的代码删去,然后使用 WinDbg(x64) 进行调试。
Solution
征得当事人同意后选取选手 Thiner 提交的解题脚本:
#!/usr/bin/python"""Solution Author: Thiner @ NeSEThe challenge is solved with some luck.""""""note hererbx=totalrcx=0rdx=buffer without npointer{}rdi=&codeinit is 32bitreturn value is check resultoffset : 0x2198virtaddr: 0x12343798code len: 0x30d3"""# gatesXgame_un.exe is the upx unpacked binaryimport capstone as cswith open('gatesXgame_un.exe') as f: raw = f.read()code = raw[0x2198:0x2198+0x30d3]# """for i in range(len(code)-5): if code[i:i+5] == '\xe8\x01\x00\x00\x00': code = code[:i+5]+'\xf1'+code[i+6:] if code[i:i+5] == '\xe8\x02\x00\x00\x00': code = code[:i+5]+'\xcd\x81'+code[i+7:] if code[i:i+5] == '\xe8\x03\x00\x00\x00': code = code[:i+5]+'\xcd\x82\xf1'+code[i+8:]# code=code.replace('e803000000e4de78'.replace())md32 = cs.Cs(cs.CS_ARCH_X86, cs.CS_MODE_32)md64 = cs.Cs(cs.CS_ARCH_X86, cs.CS_MODE_64)def dis32(addr): print ' code 32 {:#x} '.format(addr).center(80, '-') for h, i in enumerate(md32.disasm(code[addr:addr+0x100], addr)): print '{:3d}{:>#8x} {:>7s} {}'.format(h, i.address, i.mnemonic, i.op_str)def dis64(addr): print ' code 64 {:#x} '.format(addr).center(80, '-') for h, i in enumerate(md64.disasm(code[addr:addr+0x100], addr)): print '{:3d}{:>#8x} {:>7s} {}'.format(h, i.address, i.mnemonic, i.op_str)def ext32(addr): head = list(md32.disasm(code[addr:addr+0x80], addr)) # 3+2k l = [] for i in range(10): if head[3+2*i].mnemonic != 'cmp': break ch = int(head[3+2*i].op_str.split(',')[-1], 0) tar = int(head[4+2*i].op_str, 0) inst = list(md32.disasm(code[tar:tar+0x30], tar))[4] addr = int(inst.op_str.split(',')[-1], 0) l.append((ch, addr)) return ldef ext64(addr): head = list(md64.disasm(code[addr:addr+0x80], addr)) # 3+5k l = [] for i in range(10): if head[3+5*i].mnemonic != 'cmp': break ch = int(head[3+5*i].op_str.split(',')[-1], 0) tar = int(head[4+5*i].op_str, 0) inst = list(md64.disasm(code[tar:tar+0x30], tar))[5] addr = int(inst.op_str.split(',')[-1], 0) l.append((ch, addr)) return lflag = 'f4'addr = 0x558aset = set([0, 0x49e, 0x558])switch = 0def dfs(flag, addr, aset, switch): if switch == 0: # 32bit extract = ext32 else: extract = ext64 try: ca = extract(addr) except IndexError: print "flag may be" print 'npointer{'+flag+'}' return for c, a in ca: if a not in aset: dfs(flag+chr(c), a, aset | set([a]), switch ^ 1) else: #print('dead at',repr(flag)) passdfs(flag, addr, aset, switch)
3月份WEB内部赛将于3.20日开启,敬请期待