Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

湖南第二届大学生网络安全技能竞赛web解题记录

$
0
0

湖南第二届大学生网络安全技能竞赛web解题记录
0x1前言

有幸去苟了一次湖南的第二届大学生网络安全技能竞赛,除了坐大巴去湘潭大学比较累,比赛环节的待遇还是很好哒(ps.比赛现场有好多湘大漂亮的小姐姐,辛苦哒),感谢主办方精心准备的一次比赛。

回到比赛上来,这次我这个web dog真的太失败了,两道web最终没人A掉,这里我分享下当时自己的做题思路,加上赛后的复现记录。

0x2web 200 (一)解题记录

做题首先走一遍题目的流程:

上传 csv => next step=> 保存csv内容到数据库=>insert过程产生注入点

csv的格式很简单:


湖南第二届大学生网络安全技能竞赛web解题记录

在http协议里面数据就是 主要看双引号代表一个字段最优先,然后逗号分隔代表一个列

当时我通过csv的保存格式 (4,'["123","123","123","123"]','2018-12-08 12:37:24')

猜到了是insert类型的二次注入,这里会有个过滤

‘=> \‘ 导致了单引号可以逃逸,第二行第一个字段输入

123\' or sleep(5),123)# => (4,'["123\',123)#","123","123","123"]','2018-12-08 12:37:24') 这样也就变成了 (4,'["123\' or sleep(5),123) 这样理论来说就逃逸出来了

但是当时比赛的时候我本地进行测试的时候一直爆

ERROR 1292 (22007): Truncated incorrect INTEGER value:

当时我也这样带入了payload然后发现save之后也没延时,就开始怀疑自己的想法了,

当时又因为这道题没人解决出来,想着自己那么菜b应该做不出来,然后放弃了。。emm(心态爆炸那种)

最后1小时主办方给了 hint:二次注入

但是我此时已经深陷在web500中不能自拔了。

(二)赛后分析

首先说下我当时测试的时候插入错误的原因:

select @@version;

+-----------+
| @@version |
+-----------+
| 5.7.21 |
+-----------+
1 row in set (0.00 sec)

show variables like "sql_mode";

STRICT_TRANS_TABLES //严格模式

mysql 5.7.17 默认开启严格模式

有关严格模式可以可以参考下文章 MySQL sql_mode 说明(及处理一起 sql_mode 引发的问题)

作用:

STRICT_TRANS_TABLES

设置它,表示启用严格模式。

注意 STRICT_TRANS_TABLES 不是几种策略的组合,单独指 INSERT 、 UPDATE 出现少值或无效值该如何处理:

1.前面提到的把 ‘’ 传给int,严格模式下非法,若启用非严格模式则变成0,产生一个warning

2.Out Of Range,变成插入最大边界值

3.A value is missing when a new row to be inserted does not contain a value for a non-NULL column

经过我的测试发现了一些小特性:

我在低于mysql 5.7.17 的 5.6.35 测试发现:

insert into user(`user`,`pass`) values('123"'^(sleep(5)),'123')

这样是可以进行插入的,而且会忽略特殊符号,提示 warning 错误

如果是高版本下的严格模式存在特殊符号是error类型错误导致没办法进行运算,这样时间盲注就失效了

但是有趣的是

mysql> insert into test(`name`,`password`) values('"' or updatexml(1,concat(0x7e,(select user())),0),'123');
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost'

如果存在报错,那么在严格模式也是可以进行报错的,这个tips可以注意下。

之前看p神的一篇文章说到连接符导致insert出错的问题,用比较运算符来解决问题,我感觉还是有些问题的,

这个还需要去深入研究下。

最后,由于当时我没去测试题目的mysql版本也不知道具体是什么情况,也有可能是我做题的方向就已经出错了,

可能是另外的注入点,欢迎大佬找我交流下。

0x3 web500

这道题是我比赛花了很长时间,最终没解出来,感觉特别遗憾的一道题,也让我反思了自己很多问题,比赛

的经验实在是匮乏,导致拖拖拉拉,卡这卡哪,最终,拖了队友的后腿,与第一名差了15分,丢失了第一。

(一)解题记录

当时查看题目页面源代码发现注释提示了

<!--www.zip -->

下载后是两个文件: index.php valicode.php

index.php 是主要的题目文件

valicode.php 是生成验证码的文件

由于代码比较长这里只分析漏洞点的代码

想要源代码我会放上我的githud ctf_web解题记录

结合代码分析下流程:

程序功能(1-68 line):

function register($user, $pass)
function login($user, $pass)
function listnote($user)
function getnote($id, $user)
function savenote($id, $user, $title, $content)
function newnote($user, $title, $content)
function delnote($id, $user) 解题流程:

通过阅读代码发现有一处备份功能可以上传shell 但是需要admin权限,通过sql注入去获取admin权限

正向流程就是:

sql注入->获取admin密码->备份上传getshell->获取flag 漏洞点1 SQL盲注 function register($user, $pass) {
global $conn;
$user = '0x' . bin2hex($user);
$pass = '0x' . bin2hex($pass);
$result = $conn->query("select * from user where user=$user");
$data = $result->fetch_assoc();
if ($data) return false;
return $conn->query("insert into user (user,pass) values ($user,$pass)");
}
function login($user, $pass) {
global $conn;
$user = '0x' . bin2hex($user);
$result = $conn->query("select * from user where user=$user");
$data = $result->fetch_assoc();
if (!$data) return false;
if ($data['pass'] === $pass) return true;
return false;
}

前几行代码可以看出来进入sql语句的变量都做了hex编码处理

这样除了二次注入是没办法注入

结合题目的描述: 很多漏洞都是粗心引起的

然后耐心的看完了所有功能点的代码,最终发现在

function delnote($id, $user) {
global $conn;
$id = (int)$id;
$result = $conn->query("delete from note where id=$id and user='$user'");
return $result;
}

$user 没有进行hex编码拼接进了sql语句,那么回溯下看下传参过程

case 'delete':
if (!$user) {
header("HTTP/1.1 302 Found");
header("Location: ?action=login");
}
$id = (int)$_GET['id'];
var_dump($id);
delnote($id, $user); //调用delnote
header("HTTP/1.1 302 Found");
header("Location: ?action=home");

继续回溯

//69-73
$user = $_SESSION['user'];
$action = $_GET['action'];
$admin = $user === 'admin';
// $conn = mysqli_connect(DB_HOST,DB_USER,DB_PASS,DB_DATABASE) or die("connect to mysql error!");
$conn->query("set names 'utf8'"); $user 是由 $_SESSION['user'] 决定的 继续寻找下 $_SESSION['user'] 赋值 switch ($action) {
case 'login':
if ($user) {
header("HTTP/1.1 302 Found");
header("Location: ?action=home");
}
elseif (isset($_POST['user']) && isset($_POST['pass']) && isset($_POST['code'])) {
if ($_POST['code'] != $_SESSION['answer']) echo '<div class="alert alert-danger">Math Test Failed</div>';
elseif ($_POST['user'] == '') echo '<div class="alert alert-danger">Username Required</div>';
elseif ($_POST['pass'] == '') echo '<div class="alert alert-danger">Password Required</div>';
elseif (!login((string)$_POST['user'], (string)$_POST['pass'])) echo '<div class="alert alert-danger">Incorrect</div>';
else {
$_SESSION['user'] = $_POST['user']; //here
header("HTTP/1.1 302 Found");
header("Location: ?action=home");
}
$_SESSION['answer'] = rand();
}
?> $_SESSION['user'] = $_POST['user']; 可以看到在登陆成功后把post的user值直接设置给了session

那么思路就来了

通过注册一个注入的语句的用户然后去执行delnote功能,虽然输出报错信息,但是可以通过时间盲注来注入出admin的密码

但是分析流程要注意以下几个问题:

注册是否有限制:

case 'register':
if ($user) {
header("HTTP/1.1 302 Found");
header("Location: ?action=home");
}
elseif (isset($_POST['user']) && isset($_POST['pass']) && isset($_POST['code'])) {
if ($_POST['code'] != $_SESSION['answer']) echo '<div class="alert alert-danger">Math Test Failed</div>';
elseif ($_POST['user'] == '') echo '<div class="alert alert-danger">Username Required</div>';
elseif ($_POST['pass'] == '') echo '<div class="alert alert-danger">Password Required</div>';
elseif (!register((string)$_POST['user'], (string)$_POST['pass'])) echo '<div class="alert alert-danger">User Already Exists</div>';
else echo '<div class="alert alert-success">OK</div>';
$_SESSION['answer'] = rand();
} $_

Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles





Latest Images