1 /*
2 ettercap -- dissector for iSCSI CHAP authentication -- TCP 860 3260
3
4 Copyright (C) Dhiru Kholia (dhiru at openwall.com)
5
6 Tested with,
7
8 1. Dell EqualLogic target and Open-iSCSI initiator on CentOS 6.2
9 2. LIO Target and Open-iSCSI initiator, both on Arch Linux 2012
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along
22 with this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
25 */
26
27 #include <ec.h>
28 #include <ec_decode.h>
29 #include <ec_dissect.h>
30 #include <ec_session.h>
31
32 /* globals */
33
34 struct iscsi_status {
35 u_char status;
36 u_char id; /* CHAP_I */
37 u_char challenge[49]; /* CHAP_C can be 24 bytes long (48 bytes hex-encoded) */
38 };
39
40 #define WAIT_RESPONSE 1
41
42 /* protos */
43
44 FUNC_DECODER(dissector_iscsi);
45 void iscsi_init(void);
46
47 /************************************************/
48
49 /* borrowed from http://dsss.be/w/c:memmem */
_memmem(unsigned char * haystack,int hlen,char * needle,int nlen)50 static unsigned char *_memmem(unsigned char *haystack, int hlen, char *needle, int nlen)
51 {
52 int i, j = 0;
53 if (nlen > hlen)
54 return 0;
55 switch (nlen) { // we have a few specialized compares for certain needle sizes
56 case 0: // no needle? just give the haystack
57 return haystack;
58 case 1: // just use memchr for 1-byte needle
59 return memchr(haystack, needle[0], hlen);
60 case 2: // use 16-bit compares for 2-byte needles
61 for (i = 0; i < hlen - nlen + 1; i++) {
62 if (*(uint16_t *) (haystack + i) == *(uint16_t *) needle) {
63 return haystack + i;
64 }
65 }
66 break;
67 case 4: // use 32-bit compares for 4-byte needles
68 for (i = 0; i < hlen - nlen + 1; i++) {
69 if (*(uint32_t *) (haystack + i) == *(uint32_t *) needle) {
70 return haystack + i;
71 }
72 }
73 break;
74 default: // generic compare for any other needle size
75 // walk i through the haystack, matching j as long as needle[j] matches haystack[i]
76 for (i = 0; i < hlen - nlen + 1; i++) {
77 if (haystack[i] == needle[j]) {
78 if (j == nlen - 1) { // end of needle and it all matched? win.
79 return haystack + i - j;
80 } else { // keep advancing j (and i, implicitly)
81 j++;
82 }
83 } else { // no match, rewind i the length of the failed match (j), and reset j
84 i -= j;
85 j = 0;
86 }
87 }
88 }
89 return NULL;
90 }
91
92 /*
93 * this function is the initializer.
94 * it adds the entry in the table of registered decoder
95 */
96
iscsi_init(void)97 void __init iscsi_init(void)
98 {
99 dissect_add("iscsi", APP_LAYER_TCP, 860, dissector_iscsi);
100 dissect_add("iscsi", APP_LAYER_TCP, 3260, dissector_iscsi);
101 }
102
FUNC_DECODER(dissector_iscsi)103 FUNC_DECODER(dissector_iscsi)
104 {
105 DECLARE_DISP_PTR_END(ptr, end);
106 struct ec_session *s = NULL;
107 void *ident = NULL;
108 char tmp[MAX_ASCII_ADDR_LEN];
109 struct iscsi_status *conn_status;
110
111 //suppress unused warning
112 (void)end;
113 (void) DECODE_DATA;
114 (void) DECODE_DATALEN;
115 (void) DECODED_LEN;
116
117 /* Packets coming from the server */
118 if (FROM_SERVER("iscsi", PACKET)) {
119
120 /* Interesting packets have len >= 4 */
121 if (PACKET->DATA.len < 4)
122 return NULL;
123
124 dissect_create_ident(&ident, PACKET, DISSECT_CODE(dissector_iscsi));
125
126 /* if the session does not exist... */
127 if (session_get(&s, ident, DISSECT_IDENT_LEN) == -E_NOTFOUND) {
128 /* search for CHAP ID and Message Challenge */
129 char *i = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_I", 6);
130 char *c = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_C", 6);
131 if (i && c) {
132 /* create the new session */
133 dissect_create_session(&s, PACKET, DISSECT_CODE(dissector_iscsi));
134
135 /* remember the state (used later) */
136 SAFE_CALLOC(s->data, 1, sizeof(struct iscsi_status));
137
138 conn_status = (struct iscsi_status *) s->data;
139 conn_status->status = WAIT_RESPONSE;
140 conn_status->id = (u_char)atoi(i + 7);
141
142 /* CHAP_C is always null-terminated */
143 strncpy((char*)conn_status->challenge, c + 9, 48);
144 conn_status->challenge[48] = 0;
145
146 /* save the session */
147 session_put(s);
148 }
149 }
150 } else { /* Packets coming from the client */
151 dissect_create_ident(&ident, PACKET, DISSECT_CODE(dissector_iscsi));
152
153 /* Only if we catched the connection from the beginning */
154 if (session_get(&s, ident, DISSECT_IDENT_LEN) == E_SUCCESS) {
155 conn_status = (struct iscsi_status *) s->data;
156 char *n = NULL;
157 char *r = NULL;
158 if (PACKET->DATA.len > 5) {
159 n = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_N", 6);
160 r = (char*)_memmem(ptr, PACKET->DATA.len, "CHAP_R", 6);
161 }
162
163 if (conn_status->status == WAIT_RESPONSE && n && r) {
164 char user[65];
165 char response[33];
166 DEBUG_MSG("\tDissector_iscsi RESPONSE");
167 /* CHAP_R and CHAP_N are always null-terminated */
168 /* Even possibly wrong passwords can be useful,
169 * don't look for login result */
170 strncpy(response, r + 9, 32);
171 response[32] = 0;
172 strncpy(user, n + 7, 64);
173 user[64] = 0;
174 DISSECT_MSG("%s-%s-%d:$chap$%d*%s*%s\n", user, ip_addr_ntoa(&PACKET->L3.dst, tmp),
175 ntohs(PACKET->L4.dst), conn_status->id, conn_status->challenge, response);
176 dissect_wipe_session(PACKET, DISSECT_CODE(dissector_iscsi));
177 }
178 }
179 }
180
181 SAFE_FREE(ident);
182 return NULL;
183 }
184
185 // vim:ts=3:expandtab
186