Modbus协议扫描器的分析

modbus 协议分析


Modbus Packet Structure 协议格式

Trans ID ProtoID(0) Length UnitID FunctCode Data len(0-253byte)
\x00\x00 \x00\x00 \x00\x00 \x11 \x00

协议扫描代码

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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import socket
import array
import optparse
from IPy import IP
import sys

def main():

p = optparse.OptionParser( description=' Finds modbus devices in IP range and determines slave id.\nOutputs in ip:port <tab> sid format.',
prog='modscan',
version='modscan 0.1',
usage = "usage: %prog [options] IPRange")
p.add_option('--port', '-p', type='int', dest="port", default=502, help='modbus port DEFAULT:502')
p.add_option('--timeout', '-t', type='int', dest="timeout", default=500, help='socket timeout (mills) DEFAULT:500')
p.add_option('--aggressive', '-a', action ='store_true', help='continues checking past first found SID')
p.add_option('--function', '-f', type='int', dest="function", default=17, help='MODBUS Function Code DEFAULT:17')
p.add_option('--data', type='string', dest="fdata", help='MODBUS Function Data. Unicode escaped "\x00\x01"')
p.add_option('-v', '--verbose', action ='store_true', help='returns verbose output')
p.add_option('-d', '--debug', action ='store_true', help='returns extremely verbose output')

options, arguments = p.parse_args()

#make sure we have at least 1 argument (IP Addresses)
if len(arguments) == 1:

#build basic packet for this test

"""
Modbus Packet Structure
\x00\x00 \x00\x00 \x00\x00 \x11 \x00 <=================>
Trans ID ProtoID(0) Length UnitID FunctCode Data len(0-253byte)
"""

#this must be stored in a unsigned byte aray so we can make the assignment later... no string[] in python :(
rsid = array.array('B')
rsid.fromstring("\x00\x00\x00\x00\x00\x02\x01\x01")
#设置modbus功能码
#set function
rsid[7]=options.function

#add function data
if (options.fdata):
aFData = array.array('B')

#we must decode the escaped unicode before calling fromstring otherwise the literal \xXX will be interpreted
aFData.fromstring(options.fdata.decode('unicode-escape') )
rsid += aFData

#update length
rsid[5]=len(aFData)+2
#为什么还要加两个???表示很诧异

#assign IP range
iprange=IP(arguments[0])

#print friendly user message
print "Starting Scan..."

#primary loop over IP addresses
for ip in iprange:

#print str(ip)+" made it"
#loop over possible sid values (1-247)
for sid in range (1, 247):

#error messaging
fError=0
msg = str(ip)+":"+str(options.port)+"\t"+str(sid)

#print "msg="+msg

#Wrap connect in a try box
try:
#socket object instantiation
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#set socket timeout, value from cmd is in mills
s.settimeout(float(options.timeout) / float(1000))

#connect requires ip addresses in string format so it must be cast
s.connect((str(ip), options.port))

except socket.error:
#clean up
fError=1
msg += "\tFAILED TO CONNECT"
s.close()
break
#end try


#send query to device
try:
#set slave id
rsid[6]=sid

#send data to device
s.send(rsid)

except socket.error:
#failed send close socket
fError=1
msg += "\tFAILED TO SEND"
s.close()
break
#end try

try:

#recieve data
data = s.recv(1024)

except socket.timeout:
fError=1
msg += "\tFAILED TO RECV"
break
#end try

#examine response
if data:
#parse response
resp = array.array('B')
resp.fromstring(data)

if (options.debug):
print "Recieved: "+str(resp)
print (int(resp[7]) == int(options.function))

#if the function matches the one sent we are all good
if (int(resp[7]) == int(options.function)):
print msg

#in aggressive mode we keep going
if (not options.aggressive):
break

#If the function matches the one sent + 0x80 a positive response error code is detected
elif int(resp[7]) == (int(options.function)+128):
#if debug output message
msg += "\tPositive Error Response"
if (options.debug):
print msg
else:
#if debug output message
if (options.debug):
print msg
else:
fError=1
msg += "\tFAILED TO RECIEVE"
s.close()
break

#end SID for


#report based on verbosity
if (options.verbose and fError):
print msg
elif (options.debug):
print msg
#end IP for

#close socket, no longer needed
#s.shutdown(socket.SHUT_RDWR)
s.close()

print "Scan Complete."

#bad number of arguments. print help
else:
p.print_help()


if __name__ == '__main__':
try : main()
except KeyboardInterrupt:
print "Scan canceled by user."
print "Thank you for using ModScan"
except :
sys.exit()

Python执行系统命令方法

Python 执行系统命令的三种方法

Python OS 库函数

1
os.system('ls')

Python os popen 库函数

打开一个管道,然后将命令返回结果输出到日志。

1
2
popen(command [, mode='r' [, bufsize]]) -> pipe
Open a pipe to/from a command returning a file object.

具体操作:

在Python命令行下:

1
2
tmp = os.popen('ls *.py').readlines()
tmp

Python subprocess

import subprocess 
subprocess.call (["cmd", "arg1", "arg2"],shell=True)

SSH配置远程登陆问题

http://www.linuxidc.com/Linux/2015-03/114709.htm

首先更新源  
sudo apt-get update
安装ssh服务
sudo apt-get install openssh-server
检测是否已启动
ps -e | grep ssh
看到有ssh字样,说明已启动,如果没有就手动启动

/etc/init.d/ssh start
配置ssh-server,配置文件位于/etc/ssh/sshd_config,默认端口为22,为了安全,一般自定义为其他端口,然后重启
sudo /etc/init.d/ssh resart
在windows中,使用putty或者SSH Secure Shell等登录虚拟机

ssh 无密码登录要使用公钥与私钥。linux下可以用用ssh-keygen生成公钥/私钥对,下面我以CentOS为例。

有机器A(192.168.1.155),B(192.168.1.181)。现想A通过ssh免密码登录到B。
首先以root账户登陆为例。

1.在A机下生成公钥/私钥对。

[root@A ~]# ssh-keygen -t rsa

-P表示密码,-P ‘’ 就表示空密码,也可以不用-P参数,这样就要三车回车,用-P就一次回车。
该命令将在/root/.ssh目录下面产生一对密钥id_rsa和id_rsa.pub。

一般采用的ssh的rsa密钥:
id_rsa 私钥
id_rsa.pub 公钥
下述命令产生不同类型的密钥
ssh-keygen -t dsa
ssh-keygen -t rsa
ssh-keygen -t rsa1

2.把A机下的/root/.ssh/id_rsa.pub 复制到B机的 /root/.ssh/authorized_keys文件里,先要在B机上创建好 /root/.ssh 这个目录,用scp复制。(authorized_keys是一个文件,这个不是一个文件夹,需要给权限和内容)

[root@A ~]# scp /root/.ssh/id_rsa.pub root@192.168.1.181:/root/.ssh/authorized_keys
root@192.168.1.181‘s password:
id_rsa.pub 100% 223 0.2KB/s 00:00

由于还没有免密码登录的,所以要输入一次B机的root密码。

3.authorized_keys的权限要是600!!!

[root@B ~]# chmod 600 /root/.ssh/authorized_keys

4.A机登录B机。

[root@A ~]# ssh -l root 192.168.1.181
The authenticity of host ‘192.168.1.181 (192.168.1.181)’ can’t be established.
RSA key fingerprint is 00:a6:a8:87:eb:c7:40:10:39:cc:a0:eb:50:d9:6a:5b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘192.168.1.181’ (RSA) to the list of known hosts.
Last login: Thu Jul 3 09:53:18 2008 from root
[root@B ~]#

第一次登录是时要你输入yes。

现在A机可以无密码登录B机了。

小结:登录的机子可有私钥,被登录的机子要有登录机子的公钥。这个公钥/私钥对一般在私钥宿主机产生。上面是用rsa算法的公钥/私钥对,当然也可以用dsa(对应的文件是id_dsa,id_dsa.pub)

想让A,B机无密码互登录,那B机以上面同样的方式配置即可。

SSH-KeyGen 的用法

假设 A 为客户机器,B为目标机;

要达到的目的:
A机器ssh登录B机器无需输入密码;
加密方式选 rsa|dsa均可以,默认dsa

做法:
1、登录A机器
2、ssh-keygen -t [rsa|dsa],将会生成密钥文件和私钥文件 id_rsa,id_rsa.pub或id_dsa,id_dsa.pub
3、将 .pub 文件复制到B机器的 .ssh 目录, 并 cat id_dsa.pub >> ~/.ssh/authorized_keys
4、大功告成,从A机器登录B机器的目标账户,不再需要密码了;

ssh-keygen做密码验证可以使在向对方机器上ssh ,scp不用使用密码.
具体方法如下:
ssh-keygen -t rsa
然后全部回车,采用默认值.

这样生成了一对密钥,存放在用户目录的~/.ssh下。
将公钥考到对方机器的用户目录下,并拷到~/.ssh/authorized_keys中。

要保证.ssh和authorized_keys都只有用户自己有写权限。否则验证无效。(今天就是遇到这个问题,找了好久问题所在),其实仔细想想,这样做是为了不会出现系统漏洞。

Linux下堆利用学习

初识heap学习

直接上代码,结合代码学习

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
/* Per thread arena example. */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void* threadFunc(void* arg) {
printf("Before malloc in thread 1\n");
getchar();
char* addr = (char*) malloc(1000);
printf("After malloc and before free in thread 1\n");
getchar();
free(addr);
printf("After free in thread 1\n");
getchar();
}

int main() {
pthread_t t1;
void* s;
int ret;
char* addr;

printf("Welcome to per thread arena example::%d\n",getpid());
printf("Before malloc in main thread\n");
getchar();
addr = (char*) malloc(1000);
printf("After malloc and before free in main thread\n");
getchar();
free(addr);
printf("After free in main thread\n");
getchar();
ret = pthread_create(&t1, NULL, threadFunc, NULL);
if(ret)
{
printf("Thread creation error\n");
return -1;
}
ret = pthread_join(t1, &s);
if(ret)
{
printf("Thread join error\n");
return -1;
}
return 0;
}

编译阶段

gcc MyProgram.c -o MyProgram -lpthread

分阶段分析

ROPgadget 使用指南

ROPgadget 使用方法

usage:

ROPgadget [-h] [-v] [-c] [–binary ] [–opcode ]
[–string ] [–memstr ] [–depth ]
[–only ] [–filter ] [–range ]
[–badbytes ] [–rawArch ] [–rawMode ]
[–re ] [–offset ] [–ropchain] [–thumb]
[–console] [–norop] [–nojop] [–callPreceded] [–nosys]
[–multibr] [–all] [–dump]

description:

ROPgadget lets you search your gadgets on a binary. It supports several
file formats and architectures and uses the Capstone disassembler for
the search engine.

formats supported:

  • ELF
  • PE
  • Mach-O
  • Raw

architectures supported:

  • x86
  • x86-64
  • ARM
  • ARM64
  • MIPS
  • PowerPC
  • Sparc

optional arguments:

-h, –help show this help message and exit
-v, –version Display the ROPgadget’s version
-c, –checkUpdate Checks if a new version is available
–binary Specify a binary filename to analyze
–opcode Search opcode in executable segment
–string Search string in readable segment
–memstr Search each byte in all readable segment
–depth Depth for search engine (default 10)
–only Only show specific instructions
–filter Suppress specific instructions
–range Search between two addresses (0x…-0x…)
–badbytes Rejects specific bytes in the gadget’s address
–rawArch Specify an arch for a raw file
–rawMode Specify a mode for a raw file
–re Regular expression
–offset Specify an offset for gadget addresses
–ropchain Enable the ROP chain generation
–thumb Use the thumb mode for the search engine (ARM only)
–console Use an interactive console for search engine
–norop Disable ROP search engine
–nojop Disable JOP search engine
–callPreceded Only show gadgets which are call-preceded
–nosys Disable SYS search engine
–multibr Enable multiple branch gadgets
–all Disables the removal of duplicate gadgets
–dump Outputs the gadget bytes

examples:

测试用例:

ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 
#获取全部binary下的gadget组件
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --ropchain
#形成rops链
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --depth 3
#遍历深度为3,即只是遍历3层深度
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "main"
#在出现main字符串的周围检索
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string "m..n"
#在m..n这种字符串周围检索gadgets
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --opcode c9c3
#操作码是c9c3 
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|ret"
#包含mov|ret 这种模式的
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --only "mov|pop|xor|ret"
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --filter "xchg|add|sub"
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --norop --nosys
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --range 0x08041000-0x08042000
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --string main --range 0x080c9aaa-0x080c9aba
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --memstr "/bin/sh"
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --console
ROPgadget.py --binary ./test-suite-binaries/elf-Linux-x86 --badbytes "00|01-1f|7f|42"
ROPgadget.py --binary ./test-suite-binaries/Linux_lib64.so --offset 0xdeadbeef00000000
ROPgadget.py --binary ./test-suite-binaries/elf-ARMv7-ls --depth 5
ROPgadget.py --binary ./test-suite-binaries/elf-ARM64-bash --depth 5
ROPgadget.py --binary ./test-suite-binaries/raw-x86.raw --rawArch=x86 --rawMode=32

为什么文件名用小名

为什么文件名用小名?

其实说实话,这个问题我的确没有仔细想过,今天查了一下,把搜到的内容记录下。

阮一峰师傅曾写过一篇文章,在开头写过如下这段话:

“文件名建议只使用小写字母,不使用大写字母。”
“为了醒目,某些说明文件的文件名,可以使用大写字母,比如README、LICENSE。”

说实话,虽然这是 Linux 传统,我却从没认真想过原因。赶紧查资料,结果发现四个很有说服力的理由,支持这样做。

阮大神在其中提到了四点原因:

可移植性

Linux系统是大小写敏感的,而Windows、Mac系统正好相反,大小写不敏感。但是跨平台,这些都是问题。例如:abc,Abc,ABC文件名在windows、Mac、Linux系统中的识别会出现不同的问题,在Windows系统中abc不论大小写都是一样的。在Mac系统中,大小写也是不敏感的,在linux系统中,abc和ABC是不一样的文件,如果所有文件名都采用小写,具备保持良好的可移植性。

易读性

英文字母是大小写,方便读,试想看所有字母都是大小,你读着不累吗???

易用性

某些系统会生成一些预置的用户目录,采用首字母大写的目录名。比如,Ubuntu 在用户主目录会默认生成Downloads、 Pictures、Documents等目录。

另外,某些常见的配置文件或说明文件,也采用大写的文件名,比如Makefile、INSTALL、CHANGELOG、.Xclients和.Xauthority等等。

便捷性

文件名全部小写,还有利于命令行操作。比如,某些命令可以不使用-i参数了。

长亭 SQLchop 实现方法

SQL原理部分

https://blog.chaitin.cn/sqlchop-the-sqli-detection-engine/

github地址

在 8 月 5 日的 Blackhat 大会上,我们在 Arsenal 分会场做了 SQLChop 的展示,同步上线的还有 SQLChop 的网站 http://sqlchop.chaitin.com/ ,以及在 Github 放出了 SQLChop 二进制模块的下载试用 https://github.com/chaitin/sqlchop ,API 使用文档在 http://sqlchop.chaitin.com/doc.html

基于编译原理的sql处理引擎

  1. 递归解码模块
  2. 词法分析模块
  3. 语法分析模块
  4. 综合打分模块

编译语言中的完成语法语义解读SQL解读