GithubHelp home page GithubHelp logo

david942j / one_gadget Goto Github PK

View Code? Open in Web Editor NEW
2.0K 27.0 136.0 13.88 MB

The best tool for finding one gadget RCE in libc.so.6

License: MIT License

Ruby 100.00%
ctf pwnable pwn glibc one-gadget-rce shell exploit gadget libc

one_gadget's People

Contributors

cptgibbon avatar david942j avatar dependabot-preview[bot] avatar dependabot[bot] avatar inndy avatar lebr0nli avatar oshawk avatar rad4day avatar umutoztunc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

one_gadget's Issues

Version 1.8.0 defaults to posix_spawn gadgets

Seems like version 1.8.0 prefers the posix_spawn gadgets and does not list execve-gadgets with default output-level 0. Those posix_spawn gadgets have much harder contraints and shouldn't be displayed instead of the easier execve-gadgets.

Tests were conducted on the libc of the Ubuntu Docker: FROM ubuntu@sha256:a02c32cf0c2a7e8743c74deef66637aa70e063c9bd40e9e1f8c0b3ea0750b0ba

/usr/lib/x86_64-linux-gnu/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=89c3cb85f9e55046776471fed05ec441581d1969, for GNU/Linux 3.2.0, stripped

Output with one_gadget 1.7.4:

root@ecb44960ff3e:/home/ctf# one_gadget --version
OneGadget Version 1.7.4
root@ecb44960ff3e:/home/ctf# one_gadget /usr/lib/x86_64-linux-gnu/libc.so.6
0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xebcf5 execve("/bin/sh", r10, rdx)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL

0xebcf8 execve("/bin/sh", rsi, rdx)
constraints:
  address rbp-0x78 is writable
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

Output with 1.8.0:

root@ecb44960ff3e:/home/ctf# gem install one_gadget --version 1.8.0
Fetching one_gadget-1.8.0.gem
Successfully installed one_gadget-1.8.0
Parsing documentation for one_gadget-1.8.0
Installing ri documentation for one_gadget-1.8.0
Done installing documentation for one_gadget after 3 seconds
1 gem installed
root@ecb44960ff3e:/home/ctf# one_gadget --version
OneGadget Version 1.8.0
root@ecb44960ff3e:/home/ctf# one_gadget /usr/lib/x86_64-linux-gnu/libc.so.6
0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL
  rbp == NULL || (u16)[rbp] == NULL

0x10dbc2 posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x40], 0, rsp+0x70, [rsp+0xf0])
constraints:
  [rsp+0x70] == NULL
  [[rsp+0xf0]] == NULL || [rsp+0xf0] == NULL
  [rsp+0x40] == NULL || (s32)[[rsp+0x40]+0x4] <= 0

0x10dbca posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x40], 0, rsp+0x70, r9)
constraints:
  [rsp+0x70] == NULL
  [r9] == NULL || r9 == NULL
  [rsp+0x40] == NULL || (s32)[[rsp+0x40]+0x4] <= 0

0x10dbcf posix_spawn(rsp+0x64, "/bin/sh", rdx, 0, rsp+0x70, r9)
constraints:
  [rsp+0x70] == NULL
  [r9] == NULL || r9 == NULL
  rdx == NULL || (s32)[rdx+0x4] <= 0

Consider xmm* registers in symbolic execution

in libc-2.27.so on ubuntu 18.04:

   4f2ec:       48 89 44 24 08          mov    QWORD PTR [rsp+0x8],rax
   4f2f1:       0f 16 44 24 08          movhps xmm0,QWORD PTR [rsp+0x8]
   4f2f6:       0f 29 44 24 40          movaps XMMWORD PTR [rsp+0x40],xmm0

This should be considered as equivalent to mov [rsp+0x48], rax

Missing constraints on rbp

For the libc file with the hash 4fcd76645607f38d91f65654ddd6b9770b5ea54a, we have the following gadgets:
image

Here is where these gadgets are located:
image

As you can see, just before calling execve, there is an instruction mov [rbp+var_78], r11. If you use a buffer overflow to manipulate the return address with strcpy or strncpy, it is possible that you have to overwrite rbp value and you are unable to use any null bytes. Therefore, rbp+var_78 becomes an invalid address and this gadget crashes silently.

I think it should be listed under constraints and indicate that rbp+var_78 needs to be a valid and writeable address.

Some gadgets are missing

For the libc file with the hash 4fcd76645607f38d91f65654ddd6b9770b5ea54a, there are only 3 gadgets found even with the --level parameter:
image

However, there are more gadgets at location 0xE6C7E:
image

Consider posix_spawn as a gadget

Observing this on glibc 2.31 (https://gitlab.com/david942j/libcdb/blob/master/libc/libc6_2.31-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.31.so):

   87315:       48 8d 05 8b 02 13 00    lea    rax,[rip+0x13028b]        # -c
   8731c:       48 8d 0d 8c 02 13 00    lea    rcx,[rip+0x13028c]        # sh
   87323:       4c 89 e2                mov    rdx,r12
   87326:       48 89 6c 24 70          mov    QWORD PTR [rsp+0x70],rbp
   8732b:       66 48 0f 6e c8          movq   xmm1,rax
   87330:       48 8b 05 79 3b 16 00    mov    rax,QWORD PTR [rip+0x163b79]        # environ_ptr
   87337:       66 48 0f 6e c1          movq   xmm0,rcx
   8733c:       31 c9                   xor    ecx,ecx
   8733e:       66 0f 6c c1             punpcklqdq xmm0,xmm1
   87342:       48 8d bb e0 00 00 00    lea    rdi,[rbx+0xe0]
   87349:       4c 8d 44 24 60          lea    r8,[rsp+0x60]
   8734e:       31 ed                   xor    ebp,ebp
   87350:       4c 8b 08                mov    r9,QWORD PTR [rax]
   87353:       48 8d 35 50 02 13 00    lea    rsi,[rip+0x130250]        # /bin/sh
   8735a:       0f 29 44 24 60          movaps XMMWORD PTR [rsp+0x60],xmm0
   8735f:       48 c7 44 24 78 00 00 00 00      mov    QWORD PTR [rsp+0x78],0x0
   87368:       e8 73 84 08 00          call   10f7e0 <posix_spawn@@GLIBC_2.15>

I haven't tried but it looks like if we jump to 0x8731c (skip the assignment of "-c" to rax), we will execute
posix_spawn(rbx+0xe0, "/bin/sh", r12, 0, rsp+0x60, environ), and the array at rsp+0x60 is { "sh", rax, rbp, 0 }

The constraints are

  • r12 == NULL or (u32)[r12 + 4] == 0 (posix_spawn_file_actions_t.__used == 0)
  • rax == NULL

The example above is called in _IO_new_proc_open(), original source is

__posix_spawn (&((_IO_proc_file *) fp)->pid, _PATH_BSHELL, fa, 0,
               (char *const[]){ (char*) "sh", (char*) "-c",
               (char *) command, NULL }, __environ)

prototype of posix_spawn: posix_spawn (pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[])

Failed to find some gadgets on glibc 2.31

Checked with https://gitlab.com/david942j/libcdb/blob/master/libc/libc6_2.31-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.31.so

Current findings:

0xe6d43 execve("/bin/sh", r10, r12)
constraints:
  [r10] == NULL || r10 == NULL
  [r12] == NULL || r12 == NULL

0xe6d46 execve("/bin/sh", r10, rdx)
constraints:
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL

0xe6d49 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

Expected more gadgets such as:

   e6b4e:       4c 89 e2                mov    rdx,r12
   e6b51:       4c 89 fe                mov    rsi,r15
   e6b54:       48 8d 3d 4f 0a 0d 00    lea    rdi,[rip+0xd0a4f]        # 1b75aa <_libc_intl_domainname@@GLIBC_2.2.5+0x1a5>
   e6b5b:       e8 60 f6 ff ff          call   e61c0 <execve@@GLIBC_2.2.5>

0xe6b4e execve("/bin/sh", r15, r12)

this pattern is exactly same as the one at 0xe6d43, assuming a bug exists.

New Logo

Hello sir.
You have a great app, unfortunately this app does not have a logo yet, may I donate a logo for your app?

Use LibcDB BuildID Database

I am the maintainer of LibcDB (https://gitlab.com/libcdb/libcdb) which is a database of all of the libc for various distributions.

You can perform a lookup of a given libc via BuildID, SHA1, MD5, or SHA256.

https://gitlab.com/libcdb/libcdb/tree/master/hashes

Here's an example of one of the existing builds, below. If you clone the repo (warning: 30+GB) it'll be a symlink to the actual file.

https://gitlab.com/libcdb/libcdb/blob/master/hashes/build_id/369de0e1d833caa693af17f17c83ba937f0a4dad

To download the actual file directly, you can use the following link (i.e., replace blob with raw in the URL)

https://gitlab.com/libcdb/libcdb/raw/master/hashes/build_id/369de0e1d833caa693af17f17c83ba937f0a4dad

It may be useful to integrate your tool with this functionality, since LibcDB is updated daily (though there are not always new libc to actually update, it just runs as a cron job):

https://gitlab.com/libcdb/libcdb/activity

Other architectures support

Currently one_gadget is based on objdump's output, so the biggest issue is how to make users to have the specific version of objdump.

I have two ways to resolve this issue:

  1. Ask users clone the official binutils repo and compile an all-architectures-supported objdump.
    • pros: the objdump supports many architectures
    • cons: need more user-interactive actions
  2. Ask users install the specific objdump from pkg managers.
    • pros: installing things from trusted pkg managers is more comfortable to users
    • cons: will need different versions of objdump to support different architectures.

Need discussions @scwuaptx

The filename that ends with a number might cause one_gadget to fail to find some gadgets for amd64

There's a weird behavior when using one_gadget on the filename that ends with a number.
This bug can be reproduced by something like:

$ objdump --version
GNU objdump (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.
$ one_gadget --version
OneGadget Version 1.8.1
$ cp /lib/x86_64-linux-gnu/libc.so.6 .
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 > a
$ one_gadget libc.so > b
$ mv libc.so x.1337
$ one_gadget x.1337 > c
$ md5sum a
55a458aadc6b2c93fccc164ad5bedd6d  a
$ md5sum b
16db62efc9d108f06c6d2aecec205484  b
$ md5sum c
55a458aadc6b2c93fccc164ad5bedd6d  c

As you can see, although libc.so.6, libc.so, and x.1337 are actually the same file, the output of libc.so is different.


Seems like this bug is caused by this line:

dump = `#{@objdump.command(start: jmp_addr, stop: jmp_addr + 100)}|grep -E '[0-9a-f]+:'`

When the filename is something like: libc.so.6, grep -E '[0-9a-f]+:' will also match the filename in the objdump output:

$ objdump --no-show-raw-insn -w -d -M intel /lib/x86_64-linux-gnu/libc.so.6 | grep -E '[0-9a-f]+:' | head
/lib/x86_64-linux-gnu/libc.so.6:     file format elf64-x86-64
   28000:	push   QWORD PTR [rip+0x1f1002]        # 219008 <_GLOBAL_OFFSET_TABLE_+0x8>
   28006:	bnd jmp QWORD PTR [rip+0x1f1003]        # 219010 <_GLOBAL_OFFSET_TABLE_+0x10>
   2800d:	nop    DWORD PTR [rax]
   28010:	endbr64
   28014:	push   0x35
   28019:	bnd jmp 28000 <__abi_tag+0x27c5c>
   2801f:	nop
   28020:	endbr64
   28024:	push   0x34

Then it somehow causes some bugs and makes the candidates in jmp_case_candidates never pass.


This issue can be fixed by adding the --dward-start=0 option when running the objdump.
Since the description in the manual of the objdump is a little bit unclear for me, here's the source code reference that shows why --dward-start=0 works:
https://github.com/bminor/binutils-gdb/blob/a89e364b45a93acd20f48abd787ef5cb7c07f683/binutils/objdump.c#L6158-L6163
https://github.com/bminor/binutils-gdb/blob/a89e364b45a93acd20f48abd787ef5cb7c07f683/binutils/objdump.c#L5586-L5589

undefined method `tag_by_type' for nil:NilClass (NoMethodError)

Thanks for this project, but I got such error when I run the script on some stripped binary:

/home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget/fetchers/i386.rb:116:in `block in got_offset': undefined method `tag_by_type' for nil:NilClass (NoMethodError)
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget/fetchers/i386.rb:114:in `open'
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget/fetchers/i386.rb:114:in `got_offset'
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget/fetchers/i386.rb:13:in `find'
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget/fetcher.rb:45:in `from_file'
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/lib/one_gadget.rb:32:in `gadgets'
	from /home/anciety/.gem/ruby/2.4.0/gems/one_gadget-1.4.0/bin/one_gadget:48:in `<top (required)>'
	from /home/anciety/.gem/ruby/2.4.0/bin/one_gadget:22:in `load'
	from /home/anciety/.gem/ruby/2.4.0/bin/one_gadget:22:in `<main>'

Is it impossible to run this on stripped binary? According to this error message I assume it is caused by the stripping. Since I really don't know about Ruby, I can do little help with this..

And the binary is just defcon quals 2016 feedme challenge

Feature: search nearest one gadgets

Scenario

  • No information leak
  • Can partial overwrite low bytes of a GOT entry

Usage

  1. Give function names (ex. read / write / perror)
  2. Give binary, consider all GOT entries of it

Consider the content of argv array as constraints

Checked on glibc 2.31, https://gitlab.com/david942j/libcdb/blob/master/libc/libc6_2.31-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.31.so

   e6df7:       48 8d 05 ac 07 0d 00    lea    rax,[rip+0xd07ac]        # 1b75aa <_libc_intl_domainname@@GLIBC_2.2.5+0x1a5>
   e6dfe:       49 89 e3                mov    r11,rsp
   e6e01:       4c 8d 55 b0             lea    r10,[rbp-0x50]
   e6e05:       48 89 45 b0             mov    QWORD PTR [rbp-0x50],rax
   e6e09:       48 8b 45 98             mov    rax,QWORD PTR [rbp-0x68]
   e6e0d:       48 89 45 b8             mov    QWORD PTR [rbp-0x48],rax
   e6e11:       e9 25 ff ff ff          jmp    e6d3b <execvpe@@GLIBC_2.11+0x46b>
<...>
   e6d3b:       49 c7 42 10 00 00 00 00         mov    QWORD PTR [r10+0x10],0x0
   e6d43:       4c 89 e2                mov    rdx,r12
   e6d46:       4c 89 d6                mov    rsi,r10
   e6d49:       48 8d 3d 5a 08 0d 00    lea    rdi,[rip+0xd085a]        # 1b75aa <_libc_intl_domainname@@GLIBC_2.2.5+0x1a5>
   e6d50:       4c 89 5d 88             mov    QWORD PTR [rbp-0x78],r11
   e6d54:       e8 67 f4 ff ff          call   e61c0 <execve@@GLIBC_2.2.5>

It calls execve("/bin/sh", rbp-0x50, r12), the "array" of rbp-0x50 is { "/bin/sh", [rbp-0x68], 0 }, which is a valid one gadget with [rbp-0x68] == NULL as the constraint. (and rbp-0x50 has to be writable)

Annoying update check

Some time I got update check information in one_gadget -r , pretty annoying when I use python to get those offsets automatically.

I think -r option should not contain update info. Any way to avoid this exclude changing my script?

Find one-gadgets in normal binaries

Is it possible to use one_gadget on a normal binary? Sometimes in CTFs there are calls to execve("/bin/sh") somewhere in user code.

One hackish way I found to make one_gadget work on a normal binary is by converting it to a shared object with ld -r binary but that does not work with non-PIE binaries, among other possible problems :)

one_gadget can't find any gadget in my glibc

I'm trying to find gadgets on my own GLIBC using one_gagdet, but I can't seem to find anything.
Using the command like so:

$ one_gadget /usr/lib/libc.so.6

or

$ one_gadget /lib/libc.so.6

Gives no output at all. Using it on a binary that is not GLIBC gives an error so I know one_gadget is working.

GLIBC version is release 2.33.

Is it possible that there are no gadgets, am I doing something wrong, or is it a bug that I've encountered in one_gadget?

No gadgets found for BuildID[sha1]=f46739d962ec152b56d2bdb7dadaf8e576dbf6eb

Apparently this tool find no gadgets on my machine: (wrapped mine)

$ file /lib/libc-2.26.so 
/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux),
dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2,
BuildID[sha1]=f46739d962ec152b56d2bdb7dadaf8e576dbf6eb, for GNU/Linux 3.2.0, not stripped
$ ~/.gem/ruby/2.4.0/bin/one_gadget --version
OneGadget Version 1.4.0
$ ~/.gem/ruby/2.4.0/bin/one_gadget /lib/libc-2.26.so 

$ 

libc-2.26.so.zip

This build is distributed by Arch Linux glibc 2.26-5. Until next update, you can also download a copy from any Arch Linux mirror.

I don't know if it's a bug or glibc has hardened itself though.

amd64.rb:32:in `to_s': wrong number of arguments (given 1, expected 0) (ArgumentError)

file

/var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget/fetchers/amd64.rb:32:in `to_s': wrong number of arguments (given 1, expected 0) (ArgumentError)
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget/fetchers/amd64.rb:32:in `jmp_case_candidates'
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget/fetchers/amd64.rb:21:in `candidates'
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget/fetchers/base.rb:19:in `find'
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget/fetcher.rb:32:in `from_file'
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/lib/one_gadget.rb:35:in `gadgets'
    from /var/lib/gems/2.3.0/gems/one_gadget-1.6.0/bin/one_gadget:67:in `<top (required)>'
    from /usr/local/bin/one_gadget:23:in `load'
    from /usr/local/bin/one_gadget:23:in `<main>'

any suggestions?

Add offset feature .

when i use this tool , i'm bothered about having to add an offset to each gadget . Could you please add this feature to this tool ? Thanks a lot !

jmp_case_candidates() "bad value for range" error

Some nonstandard versions of GLIBC can cause one_gadget v1.8.0 and later to error with "bad value for range" in jmp_case_candidates() from lib/one_gadget/fetchers/amd64.rb

git bisect suggests this problem was introduced in commit 8765130

The problem seems to be a change to this line:

`#{@objdump.command}|egrep '# #{bin_sh_hex}' -A 8`.split('--').map do |cand|

Where the -A argument passed to egrep was changed from 3 to 8. Anything below 8 doesn't seem to cause problems, but 8 will trigger the error later in the function.

I'm not a Ruby developer so this is as far as I could take this issue. I also appreciate this only appears to affect nonstandard GLIBC builds, so I understand if it's not a priority. Attached is a GLIBC build that triggers this bug: libc-2.30.zip

Thanks for building this great tool and for making it available!

EDIT: After further testing, this problem seems to only manifest in Ubuntu 18.04, later versions of the same OS aren't affected. Perhaps this points to a binutils issue?

one_gadget requires Ruby version >= 2.4.

here is my problem,

ERROR: Error installing one_gadget:
one_gadget requires Ruby version >= 2.4.

when I tried gem install one_gadget,it appeared. but in article it only need ruby version >= 2.1.0, by the way ,my os is Ubuntu 16.04.7 LTS and ruby version is ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu] , save me plz!

GOT address of libc

What does it mean by GOT address of libc in one_gadget's output?
E.g>
`
0x3d123 execve("/bin/sh", esp+0x34, environ)
constraints:
esi is the GOT address of libc
[esp+0x34] == NULL

0x3d125 execve("/bin/sh", esp+0x38, environ)
constraints:
esi is the GOT address of libc
[esp+0x38] == NULL

0x3d129 execve("/bin/sh", esp+0x3c, environ)
constraints:
esi is the GOT address of libc
[esp+0x3c] == NULL

0x3d130 execve("/bin/sh", esp+0x40, environ)
constraints:
esi is the GOT address of libc
[esp+0x40] == NULL

0x67b4f execl("/bin/sh", eax)
constraints:
esi is the GOT address of libc
eax == NULL

0x67b50 execl("/bin/sh", [esp])
constraints:
esi is the GOT address of libc
[esp] == NULL

0x1380be execl("/bin/sh", eax)
constraints:
ebx is the GOT address of libc
eax == NULL

0x1380bf execl("/bin/sh", [esp])
constraints:
ebx is the GOT address of libc
[esp] == NULL
`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.