#!/usr/bin/env python # -*- coding: utf-8 -*- # # TODO: # - check if everything is filled in (computers, params, preparation) # - improve scoring # - test # - switch to a real SSH/SFTP client to properly handle filenames instructions = { 'si': u''' Postavite dva navidezna računalnika - SimpleArbiter in Student. Oba naj bosta povezana na internet. Poleg tega mora biti Student na naslovu {student_IP} dostopen s SimpleArbiter. Računajte, da se na Student ob zagonu zažene program {net_prog_name}, ki vam spreminja nastavitve mrežne kartice. V domačem imeniku uporabnika student obstaja program {P_c} v programskem jeziku C. Prevedite ga v program z imenom {P_executable}. Izvorna koda je namenoma pokvarjena tako, da so vanjo vrinjeni nepotrebni znaki. Pred prevajanjem jo morate popraviti. Napišite skripto ali program {P_script} v domačem imeniku uporabnika student, ki: - požene {P_executable} z argumentom "{arg_c}" in mu na standardni vhod pripelje vrednost spremenljivke {env_c} - vse, kar {P_executable} izpiše na stderr, spravi v datoteko {out_stderr_c}. - vse vrstice, ki jih {P_executable} izpiše na stdout in vsebujejo zaporedje znakov "ma", zapiše v {out_stdout_c}. Lastnik vseh ustvarjenih datotek mora biti uporabnik student. Gesla uporabnika student (vaje) ne smete spreminjati.''', 'en': u'''Set up two virtual machines - SimpleArbiter and Student. Both should be connected to the Internet. Student should also be accessible from SimpleArbiter at the address {student_IP}. Keep in mind that a program called {net_prog_name} starts on Student whenever Student boots. This program may change your network settings. There is a program called {P_c} in the home directory of student. Compile it into a program called {P_executable}. The source code is intentionally broken so that unneccessarry characters are inserted into the file. You have to fix the file before compiling. Also, write a script or program called {P_script} in the home directory of student. The script should: - run {P_executable} with the argument {arg_c} and pipe the value of the environment variable {env_c} into {P_executable}'s standard input. - redirect stderr of {P_executable} into a file called {out_stderr_c}. - save each line which {P_executable} outputs and which contains the character sequence "ma" into {out_stdout_c}. The owner of all created files should be student. You are not allowed to change student's password (vaje). ''', } computers = { 'SimpleArbiter': { 'disks': [ { 'name': 'simpleArbiter', }, ], 'network_interfaces': [ {'network': 'net1'}, {'network': 'net2'}, ], 'flavor': 'm1.tiny', 'config_drive': True, }, 'Student': { 'disks': [ {'name': 'student-entrance'} ], 'flavor': 'm1.tiny', 'network_interfaces': [{'network': 'net1'}, {'network': 'net3'}], 'config_drive': True, } } networks = { 'net1': { 'public': False, }, 'net2': { 'public': True, }, 'net3': { 'public': True, } } params_meta = { 'student_IP': { 'descriptions': { 'si': 'IP naslov SimpleStudent', 'en': 'IP address of SimpleStudent', }, 'w': False, 'public': True, 'type': 'IP', 'generated': True, }, 'net_prog_name': { 'descriptions': { 'si': 'Ime programa, ki ponastalvlja naslov', 'en': 'The name of the program resetting the network' }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'P_c': { 'descriptions': { 'si': 'Datoteka s programom v C', 'en': 'Filename of the program in C', }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'P_executable': { 'descriptions': { 'si': 'Ime prevedenega programa v C', 'en': 'Filename of the compiled C program' }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'arg_c': { 'descriptions': { 'si': 'Vrednost argumenta', 'en': 'Argument value', }, 'w': False, 'public': True, 'type': 'short_text', 'generated': True, }, 'env_c': { 'descriptions': { 'si': 'Ime okoljske spremenljivke', 'en': 'The name of the environment environment', }, 'w': False, 'public': True, 'type': 'short_text', 'generated': True, }, 'out_stderr_c': { 'descriptions': { 'si': 'Datoteka z napakami', 'en': 'File to store errors', }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'P_script': { 'descriptions': { 'si': 'Ime skripte', 'en': 'Filename of the script', }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'out_stdout_c': { 'descriptions': { 'si': 'Datoteka z izhodom', 'en': 'File to store the output', }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, }, 'param_gen_seed': { 'descriptions': { 'si': 'Nakljucno seme', 'en': 'Random seed', }, 'w': False, 'public': True, 'type': None, 'generated': True, }, 'c_destroy_gen_seed': { 'descriptions': { 'si': 'Nakljucno seme za kvarjenje kode v C', 'en': 'Random seed for destroying the C code', }, 'w': False, 'public': False, 'type': None, 'generated': True, } } def task(student_IP, net_prog_name, P_c, P_executable, arg_c, env_c, out_stderr_c, out_stdout_c, P_script, param_gen_seed): from pexpect import pxssh import random conn = pxssh.pxssh() conn.login(student_IP, 'student', 'vaje') results = dict() r = random.Random(int(param_gen_seed)) env_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(11)]) arg_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(13)]) stdin_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(17)]) conn.sendline('ls -l {}'.format(P_script)) conn.prompt() results['script_ls'] = conn.before conn.sendline('ls -l {}'.format(P_executable)) conn.prompt() results['executable_ls'] = conn.before conn.sendline('export {}={}; {}'.format(env_c, env_val, P_script)) conn.prompt() results['script_run'] = conn.before conn.sendline('cat {}'.format(out_stderr_c)) conn.prompt() results['script_stderr'] = conn.before conn.sendline('cat {}'.format(out_stdout_c)) conn.prompt() results['script_stdout'] = conn.before conn.sendline('echo "{}" | {} "{}" 2> /dev/null'.format(stdin_val, P_executable, arg_val)) conn.prompt() results['prog_stdout'] = conn.before conn.sendline('echo "{}" | {} "{}" > /dev/null'.format(stdin_val, P_executable, arg_val)) conn.prompt() results['prog_stderr'] = conn.before conn.logout() return results def gen_params(user_id, params_meta): import random r = random.Random(user_id+'evil cornholio') params = kpov_random_helpers.default_gen(user_id, params_meta) homedir = '/home/student/' params['env_c'] = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ') for i in xrange(5)]) params['P_c'] = "".join([r.choice('abcdefghijklmnoprst') for i in xrange(5)]) + ".c" params['param_gen_seed'] = str(r.randint(0, 2**24)) params['c_destroy_gen_seed'] = str(r.randint(0, 2**24)) dest_net = kpov_random_helpers.IPv4_subnet_gen(r, '10.0.2.128/26', 26) params['student_IP'] = kpov_random_helpers.IPv4_addr_gen(r, dest_net)[0] for k in ['P_c', 'P_executable', 'out_stderr_c', 'P_script', 'out_stdout_c']: params[k] = homedir + params[k] return params def task_check(results, params): import os def test_out_gen(arg, var): s_out = "" s_err = "" r = 0 arg_len = len(arg) env_len = len(var) for i in xrange(100): s_out += chr(32 + ((ord(arg[i % arg_len]) ^ ord(var[i % env_len])) % 64)) r += ord(arg[i % arg_len]) + ord(var[i % env_len]) + i; if (i % 17 == 0): s_out += "RAUS\r\n"; if (i % 29 == 0): s_out += 'ma' s_err += chr((r % 31) + ord('A')); if (i % 23 == 0): s_err += "PATACIS\r\n" retval = r % 16 s_err += '\r\n' s_out += '\r\n' return(s_out, s_err, retval) score = 0 hints = [] r = random.Random(int(params['param_gen_seed'])) env_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(11)]) arg_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(13)]) stdin_val = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ012345') for i in xrange(17)]) expected_script_stdout, expected_script_stderr, rval = test_out_gen( params['arg_c'], env_val ) expected_script_stderr = 'cat {}\r\n'.format(params['out_stderr_c']) + expected_script_stderr # hints += [expected_script_stderr, results['script_run'], results['script_stderr'], params['arg_c'], env_val] if expected_script_stderr != results['script_stderr']: hints += ['wrong script stderr'] else: score += 2 split_stdout = expected_script_stdout.split('\r\n') expected_script_stdout = "\r\n".join([ i for i in split_stdout if i.find('ma') >= 0]) expected_script_stdout = 'cat {}\r\n'.format(params['out_stdout_c']) + expected_script_stdout + "\r\n" if expected_script_stdout != results['script_stdout']: hints += ['wrong script stdout'] else: score += 2 expected_prog_stdout, expected_prog_stderr, rval = test_out_gen( arg_val, stdin_val ) if expected_prog_stderr != results['prog_stderr'][-len(expected_prog_stderr):]: hints += ['wrong program stderr'] else: score += 2 if expected_prog_stdout != results['prog_stdout'][-len(expected_prog_stdout):]: hints += ['wrong program stdout'] else: score += 2 if results['script_ls'].find('-r') < 0: hints += ['script not found'] else: score += 1 if results['executable_ls'].find('xr') < 0: hints += ['C executable not found'] else: score += 1 return score, hints def prepare_disks(templates, task_params, global_params): c_source = '''#include #include #include /* Odstranite vse odvecne velike crke Q, W ali X in program se bo prevedel. */ int main(int argc, char **argv){ unsigned char *arg; unsigned char var[255]; int i, arg_len, env_len, r; scanf("%s", var); arg = argv[1]; arg_len = strlen(argv[1]); env_len = strlen(var); r = 0; for (i = 0; i<100; i++){ printf("%c", 32 + (arg[i % arg_len] ^ var[i % env_len]) % 64); r += (int)arg[i % arg_len] + (int)var[i % env_len] + i; if (i % 17 == 0){ printf("RAUS\\n"); } if (i % 29 == 0){ printf("ma"); } fprintf(stderr, "%c", (r % 31) + 'A'); if (i % 23 == 0){ fprintf(stderr, "PATACIS\\n"); } } printf("\\n"); fprintf(stderr, "\\n"); return r % 16; } ''' evil_shell_source = """#!/bin/bash -e while true; do /sbin/ifconfig eth1 10.0.4.19 2> /dev/null; /sbin/ifconfig eth0 10.0.4.20 2> /dev/null; /sbin/ifconfig eth2 10.0.4.21 2> /dev/null; /sbin/ifconfig en0p3 10.0.4.19 2> /dev/null; /sbin/ifconfig en0p8 10.0.4.20 2> /dev/null; /sbin/ifconfig enp0s3 10.0.4.21 2> /dev/null; /sbin/ifconfig enp0s8 10.0.4.21 2> /dev/null; sleep 10; done; """ import random d = templates['student-entrance'] r = random.Random(task_params['c_destroy_gen_seed']) destroyed_c_source = c_source[:110] for c in c_source[110:]: i = r.randint(0, 5) if i == 1: destroyed_c_source += 'QW' if i == 2: destroyed_c_source += 'XW' if i == 3: destroyed_c_source += 'QX' destroyed_c_source += c d.write(task_params['P_c'], destroyed_c_source) d.chown(1000, 1000, task_params['P_c']) sh_path = r.choice(['/usr/share/doc', '/var/lib', '/usr/local/share', '/etc/alternatives']) sh_file = sh_path + '/' + task_params['net_prog_name'] d.write(sh_file, evil_shell_source) d.chmod(0775, sh_file) d.write("/etc/rc.local", """#!/bin/sh -e export PATH=$PATH:{} nohup {} & exit 0 """.format(sh_path, task_params['net_prog_name']))